zopfli 0.0.2 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +6 -14
- data/.gitmodules +1 -1
- data/.travis.yml +23 -0
- data/Gemfile +2 -0
- data/README.md +6 -1
- data/Rakefile +8 -10
- data/ext/extconf.rb +2 -1
- data/ext/zopfli.c +39 -20
- data/lib/zopfli/version.rb +1 -1
- data/smoke.sh +9 -0
- data/{test → spec}/fixtures/alice29.txt +0 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/zopfli_spec.rb +68 -0
- data/vendor/zopfli/{blocksplitter.c → src/zopfli/blocksplitter.c} +41 -53
- data/vendor/zopfli/{blocksplitter.h → src/zopfli/blocksplitter.h} +2 -6
- data/vendor/zopfli/{cache.c → src/zopfli/cache.c} +6 -0
- data/vendor/zopfli/{cache.h → src/zopfli/cache.h} +0 -0
- data/vendor/zopfli/src/zopfli/deflate.c +931 -0
- data/vendor/zopfli/{deflate.h → src/zopfli/deflate.h} +17 -2
- data/vendor/zopfli/src/zopfli/gzip_container.c +124 -0
- data/vendor/zopfli/{gzip_container.h → src/zopfli/gzip_container.h} +8 -0
- data/vendor/zopfli/{hash.c → src/zopfli/hash.c} +18 -10
- data/vendor/zopfli/{hash.h → src/zopfli/hash.h} +10 -7
- data/vendor/zopfli/{katajainen.c → src/zopfli/katajainen.c} +73 -62
- data/vendor/zopfli/{katajainen.h → src/zopfli/katajainen.h} +1 -1
- data/vendor/zopfli/{lz77.c → src/zopfli/lz77.c} +190 -42
- data/vendor/zopfli/{lz77.h → src/zopfli/lz77.h} +39 -23
- data/vendor/zopfli/{squeeze.c → src/zopfli/squeeze.c} +75 -61
- data/vendor/zopfli/{squeeze.h → src/zopfli/squeeze.h} +1 -0
- data/vendor/zopfli/{util.c → src/zopfli/symbols.h} +49 -23
- data/vendor/zopfli/{tree.c → src/zopfli/tree.c} +0 -0
- data/vendor/zopfli/{tree.h → src/zopfli/tree.h} +0 -0
- data/vendor/zopfli/src/zopfli/util.c +35 -0
- data/vendor/zopfli/{util.h → src/zopfli/util.h} +6 -23
- data/vendor/zopfli/{zlib_container.c → src/zopfli/zlib_container.c} +1 -1
- data/vendor/zopfli/{zlib_container.h → src/zopfli/zlib_container.h} +8 -0
- data/vendor/zopfli/{zopfli.h → src/zopfli/zopfli.h} +10 -4
- data/vendor/zopfli/{zopfli_bin.c → src/zopfli/zopfli_bin.c} +31 -15
- data/vendor/zopfli/{zopfli_lib.c → src/zopfli/zopfli_lib.c} +1 -2
- data/zopfli.gemspec +9 -28
- metadata +51 -50
- data/test/test_zopfli_deflate.rb +0 -47
- data/vendor/zopfli/CONTRIBUTORS +0 -6
- data/vendor/zopfli/README +0 -25
- data/vendor/zopfli/deflate.c +0 -698
- data/vendor/zopfli/gzip_container.c +0 -117
- data/vendor/zopfli/makefile +0 -5
@@ -25,8 +25,13 @@ Functions to compress according to the DEFLATE specification, using the
|
|
25
25
|
"squeeze" LZ77 compression backend.
|
26
26
|
*/
|
27
27
|
|
28
|
+
#include "lz77.h"
|
28
29
|
#include "zopfli.h"
|
29
30
|
|
31
|
+
#ifdef __cplusplus
|
32
|
+
extern "C" {
|
33
|
+
#endif
|
34
|
+
|
30
35
|
/*
|
31
36
|
Compresses according to the deflate specification and append the compressed
|
32
37
|
result to the output.
|
@@ -71,7 +76,17 @@ dists: ll77 distances
|
|
71
76
|
lstart: start of block
|
72
77
|
lend: end of block (not inclusive)
|
73
78
|
*/
|
74
|
-
double ZopfliCalculateBlockSize(const
|
75
|
-
const unsigned short* dists,
|
79
|
+
double ZopfliCalculateBlockSize(const ZopfliLZ77Store* lz77,
|
76
80
|
size_t lstart, size_t lend, int btype);
|
81
|
+
|
82
|
+
/*
|
83
|
+
Calculates block size in bits, automatically using the best btype.
|
84
|
+
*/
|
85
|
+
double ZopfliCalculateBlockSizeAutoType(const ZopfliLZ77Store* lz77,
|
86
|
+
size_t lstart, size_t lend);
|
87
|
+
|
88
|
+
#ifdef __cplusplus
|
89
|
+
} // extern "C"
|
90
|
+
#endif
|
91
|
+
|
77
92
|
#endif /* ZOPFLI_DEFLATE_H_ */
|
@@ -0,0 +1,124 @@
|
|
1
|
+
/*
|
2
|
+
Copyright 2013 Google Inc. All Rights Reserved.
|
3
|
+
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
you may not use this file except in compliance with the License.
|
6
|
+
You may obtain a copy of the License at
|
7
|
+
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
See the License for the specific language governing permissions and
|
14
|
+
limitations under the License.
|
15
|
+
|
16
|
+
Author: lode.vandevenne@gmail.com (Lode Vandevenne)
|
17
|
+
Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
|
18
|
+
*/
|
19
|
+
|
20
|
+
#include "gzip_container.h"
|
21
|
+
#include "util.h"
|
22
|
+
|
23
|
+
#include <stdio.h>
|
24
|
+
|
25
|
+
#include "deflate.h"
|
26
|
+
|
27
|
+
/* CRC polynomial: 0xedb88320 */
|
28
|
+
static const unsigned long crc32_table[256] = {
|
29
|
+
0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u,
|
30
|
+
3915621685u, 2657392035u, 249268274u, 2044508324u, 3772115230u, 2547177864u,
|
31
|
+
162941995u, 2125561021u, 3887607047u, 2428444049u, 498536548u, 1789927666u,
|
32
|
+
4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u,
|
33
|
+
325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u,
|
34
|
+
4195302755u, 2366115317u, 997073096u, 1281953886u, 3579855332u, 2724688242u,
|
35
|
+
1006888145u, 1258607687u, 3524101629u, 2768942443u, 901097722u, 1119000684u,
|
36
|
+
3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u,
|
37
|
+
651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u,
|
38
|
+
3485111705u, 3099436303u, 671266974u, 1594198024u, 3322730930u, 2970347812u,
|
39
|
+
795835527u, 1483230225u, 3244367275u, 3060149565u, 1994146192u, 31158534u,
|
40
|
+
2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u,
|
41
|
+
2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u,
|
42
|
+
2439277719u, 3865271297u, 1802195444u, 476864866u, 2238001368u, 4066508878u,
|
43
|
+
1812370925u, 453092731u, 2181625025u, 4111451223u, 1706088902u, 314042704u,
|
44
|
+
2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u,
|
45
|
+
1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u,
|
46
|
+
2765210733u, 3554079995u, 1131014506u, 879679996u, 2909243462u, 3663771856u,
|
47
|
+
1141124467u, 855842277u, 2852801631u, 3708648649u, 1342533948u, 654459306u,
|
48
|
+
3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u,
|
49
|
+
1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u,
|
50
|
+
3082640443u, 3233442989u, 3988292384u, 2596254646u, 62317068u, 1957810842u,
|
51
|
+
3939845945u, 2647816111u, 81470997u, 1943803523u, 3814918930u, 2489596804u,
|
52
|
+
225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u,
|
53
|
+
4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u,
|
54
|
+
426522225u, 1852507879u, 4275313526u, 2312317920u, 282753626u, 1742555852u,
|
55
|
+
4189708143u, 2394877945u, 397917763u, 1622183637u, 3604390888u, 2714866558u,
|
56
|
+
953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u,
|
57
|
+
3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u,
|
58
|
+
829329135u, 1181335161u, 3412177804u, 3160834842u, 628085408u, 1382605366u,
|
59
|
+
3423369109u, 3138078467u, 570562233u, 1426400815u, 3317316542u, 2998733608u,
|
60
|
+
733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u,
|
61
|
+
2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u,
|
62
|
+
1913087877u, 83908371u, 2512341634u, 3803740692u, 2075208622u, 213261112u,
|
63
|
+
2463272603u, 3855990285u, 2094854071u, 198958881u, 2262029012u, 4057260610u,
|
64
|
+
1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u,
|
65
|
+
2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u,
|
66
|
+
1634467795u, 376229701u, 2685067896u, 3608007406u, 1308918612u, 956543938u,
|
67
|
+
2808555105u, 3495958263u, 1231636301u, 1047427035u, 2932959818u, 3654703836u,
|
68
|
+
1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u,
|
69
|
+
3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u,
|
70
|
+
1423857449u, 601450431u, 3009837614u, 3294710456u, 1567103746u, 711928724u,
|
71
|
+
3020668471u, 3272380065u, 1510334235u, 755167117u
|
72
|
+
};
|
73
|
+
|
74
|
+
/* Returns the CRC32 */
|
75
|
+
static unsigned long CRC(const unsigned char* data, size_t size) {
|
76
|
+
unsigned long result = 0xffffffffu;
|
77
|
+
for (; size > 0; size--) {
|
78
|
+
result = crc32_table[(result ^ *(data++)) & 0xff] ^ (result >> 8);
|
79
|
+
}
|
80
|
+
return result ^ 0xffffffffu;
|
81
|
+
}
|
82
|
+
|
83
|
+
/* Compresses the data according to the gzip specification, RFC 1952. */
|
84
|
+
void ZopfliGzipCompress(const ZopfliOptions* options,
|
85
|
+
const unsigned char* in, size_t insize,
|
86
|
+
unsigned char** out, size_t* outsize) {
|
87
|
+
unsigned long crcvalue = CRC(in, insize);
|
88
|
+
unsigned char bp = 0;
|
89
|
+
|
90
|
+
ZOPFLI_APPEND_DATA(31, out, outsize); /* ID1 */
|
91
|
+
ZOPFLI_APPEND_DATA(139, out, outsize); /* ID2 */
|
92
|
+
ZOPFLI_APPEND_DATA(8, out, outsize); /* CM */
|
93
|
+
ZOPFLI_APPEND_DATA(0, out, outsize); /* FLG */
|
94
|
+
/* MTIME */
|
95
|
+
ZOPFLI_APPEND_DATA(0, out, outsize);
|
96
|
+
ZOPFLI_APPEND_DATA(0, out, outsize);
|
97
|
+
ZOPFLI_APPEND_DATA(0, out, outsize);
|
98
|
+
ZOPFLI_APPEND_DATA(0, out, outsize);
|
99
|
+
|
100
|
+
ZOPFLI_APPEND_DATA(2, out, outsize); /* XFL, 2 indicates best compression. */
|
101
|
+
ZOPFLI_APPEND_DATA(3, out, outsize); /* OS follows Unix conventions. */
|
102
|
+
|
103
|
+
ZopfliDeflate(options, 2 /* Dynamic block */, 1,
|
104
|
+
in, insize, &bp, out, outsize);
|
105
|
+
|
106
|
+
/* CRC */
|
107
|
+
ZOPFLI_APPEND_DATA(crcvalue % 256, out, outsize);
|
108
|
+
ZOPFLI_APPEND_DATA((crcvalue >> 8) % 256, out, outsize);
|
109
|
+
ZOPFLI_APPEND_DATA((crcvalue >> 16) % 256, out, outsize);
|
110
|
+
ZOPFLI_APPEND_DATA((crcvalue >> 24) % 256, out, outsize);
|
111
|
+
|
112
|
+
/* ISIZE */
|
113
|
+
ZOPFLI_APPEND_DATA(insize % 256, out, outsize);
|
114
|
+
ZOPFLI_APPEND_DATA((insize >> 8) % 256, out, outsize);
|
115
|
+
ZOPFLI_APPEND_DATA((insize >> 16) % 256, out, outsize);
|
116
|
+
ZOPFLI_APPEND_DATA((insize >> 24) % 256, out, outsize);
|
117
|
+
|
118
|
+
if (options->verbose) {
|
119
|
+
fprintf(stderr,
|
120
|
+
"Original Size: %d, Gzip: %d, Compression: %f%% Removed\n",
|
121
|
+
(int)insize, (int)*outsize,
|
122
|
+
100.0 * (double)(insize - *outsize) / (double)insize);
|
123
|
+
}
|
124
|
+
}
|
@@ -26,6 +26,10 @@ Functions to compress according to the Gzip specification.
|
|
26
26
|
|
27
27
|
#include "zopfli.h"
|
28
28
|
|
29
|
+
#ifdef __cplusplus
|
30
|
+
extern "C" {
|
31
|
+
#endif
|
32
|
+
|
29
33
|
/*
|
30
34
|
Compresses according to the gzip specification and append the compressed
|
31
35
|
result to the output.
|
@@ -39,4 +43,8 @@ void ZopfliGzipCompress(const ZopfliOptions* options,
|
|
39
43
|
const unsigned char* in, size_t insize,
|
40
44
|
unsigned char** out, size_t* outsize);
|
41
45
|
|
46
|
+
#ifdef __cplusplus
|
47
|
+
} // extern "C"
|
48
|
+
#endif
|
49
|
+
|
42
50
|
#endif /* ZOPFLI_GZIP_H_ */
|
@@ -26,13 +26,26 @@ Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala)
|
|
26
26
|
#define HASH_SHIFT 5
|
27
27
|
#define HASH_MASK 32767
|
28
28
|
|
29
|
-
void
|
30
|
-
size_t i;
|
31
|
-
|
32
|
-
h->val = 0;
|
29
|
+
void ZopfliAllocHash(size_t window_size, ZopfliHash* h) {
|
33
30
|
h->head = (int*)malloc(sizeof(*h->head) * 65536);
|
34
31
|
h->prev = (unsigned short*)malloc(sizeof(*h->prev) * window_size);
|
35
32
|
h->hashval = (int*)malloc(sizeof(*h->hashval) * window_size);
|
33
|
+
|
34
|
+
#ifdef ZOPFLI_HASH_SAME
|
35
|
+
h->same = (unsigned short*)malloc(sizeof(*h->same) * window_size);
|
36
|
+
#endif
|
37
|
+
|
38
|
+
#ifdef ZOPFLI_HASH_SAME_HASH
|
39
|
+
h->head2 = (int*)malloc(sizeof(*h->head2) * 65536);
|
40
|
+
h->prev2 = (unsigned short*)malloc(sizeof(*h->prev2) * window_size);
|
41
|
+
h->hashval2 = (int*)malloc(sizeof(*h->hashval2) * window_size);
|
42
|
+
#endif
|
43
|
+
}
|
44
|
+
|
45
|
+
void ZopfliResetHash(size_t window_size, ZopfliHash* h) {
|
46
|
+
size_t i;
|
47
|
+
|
48
|
+
h->val = 0;
|
36
49
|
for (i = 0; i < 65536; i++) {
|
37
50
|
h->head[i] = -1; /* -1 indicates no head so far. */
|
38
51
|
}
|
@@ -42,7 +55,6 @@ void ZopfliInitHash(size_t window_size, ZopfliHash* h) {
|
|
42
55
|
}
|
43
56
|
|
44
57
|
#ifdef ZOPFLI_HASH_SAME
|
45
|
-
h->same = (unsigned short*)malloc(sizeof(*h->same) * window_size);
|
46
58
|
for (i = 0; i < window_size; i++) {
|
47
59
|
h->same[i] = 0;
|
48
60
|
}
|
@@ -50,9 +62,6 @@ void ZopfliInitHash(size_t window_size, ZopfliHash* h) {
|
|
50
62
|
|
51
63
|
#ifdef ZOPFLI_HASH_SAME_HASH
|
52
64
|
h->val2 = 0;
|
53
|
-
h->head2 = (int*)malloc(sizeof(*h->head2) * 65536);
|
54
|
-
h->prev2 = (unsigned short*)malloc(sizeof(*h->prev2) * window_size);
|
55
|
-
h->hashval2 = (int*)malloc(sizeof(*h->hashval2) * window_size);
|
56
65
|
for (i = 0; i < 65536; i++) {
|
57
66
|
h->head2[i] = -1;
|
58
67
|
}
|
@@ -129,7 +138,6 @@ void ZopfliUpdateHash(const unsigned char* array, size_t pos, size_t end,
|
|
129
138
|
|
130
139
|
void ZopfliWarmupHash(const unsigned char* array, size_t pos, size_t end,
|
131
140
|
ZopfliHash* h) {
|
132
|
-
(void)end;
|
133
141
|
UpdateHashValue(h, array[pos + 0]);
|
134
|
-
UpdateHashValue(h, array[pos + 1]);
|
142
|
+
if (pos + 1 < end) UpdateHashValue(h, array[pos + 1]);
|
135
143
|
}
|
@@ -27,16 +27,16 @@ The hash for ZopfliFindLongestMatch of lz77.c.
|
|
27
27
|
#include "util.h"
|
28
28
|
|
29
29
|
typedef struct ZopfliHash {
|
30
|
-
int* head; /* Hash value to index of its most recent
|
31
|
-
unsigned short* prev; /* Index to index of prev.
|
30
|
+
int* head; /* Hash value to index of its most recent occurrence. */
|
31
|
+
unsigned short* prev; /* Index to index of prev. occurrence of same hash. */
|
32
32
|
int* hashval; /* Index to hash value at this index. */
|
33
33
|
int val; /* Current hash value. */
|
34
34
|
|
35
35
|
#ifdef ZOPFLI_HASH_SAME_HASH
|
36
36
|
/* Fields with similar purpose as the above hash, but for the second hash with
|
37
37
|
a value that is calculated differently. */
|
38
|
-
int* head2; /* Hash value to index of its most recent
|
39
|
-
unsigned short* prev2; /* Index to index of prev.
|
38
|
+
int* head2; /* Hash value to index of its most recent occurrence. */
|
39
|
+
unsigned short* prev2; /* Index to index of prev. occurrence of same hash. */
|
40
40
|
int* hashval2; /* Index to hash value at this index. */
|
41
41
|
int val2; /* Current hash value. */
|
42
42
|
#endif
|
@@ -46,10 +46,13 @@ typedef struct ZopfliHash {
|
|
46
46
|
#endif
|
47
47
|
} ZopfliHash;
|
48
48
|
|
49
|
-
/* Allocates
|
50
|
-
void
|
49
|
+
/* Allocates ZopfliHash memory. */
|
50
|
+
void ZopfliAllocHash(size_t window_size, ZopfliHash* h);
|
51
51
|
|
52
|
-
/*
|
52
|
+
/* Resets all fields of ZopfliHash. */
|
53
|
+
void ZopfliResetHash(size_t window_size, ZopfliHash* h);
|
54
|
+
|
55
|
+
/* Frees ZopfliHash memory. */
|
53
56
|
void ZopfliCleanHash(ZopfliHash* h);
|
54
57
|
|
55
58
|
/*
|
@@ -26,6 +26,7 @@ Jyrki Katajainen, Alistair Moffat, Andrew Turpin".
|
|
26
26
|
#include "katajainen.h"
|
27
27
|
#include <assert.h>
|
28
28
|
#include <stdlib.h>
|
29
|
+
#include <limits.h>
|
29
30
|
|
30
31
|
typedef struct Node Node;
|
31
32
|
|
@@ -36,16 +37,13 @@ struct Node {
|
|
36
37
|
size_t weight; /* Total weight (symbol count) of this chain. */
|
37
38
|
Node* tail; /* Previous node(s) of this chain, or 0 if none. */
|
38
39
|
int count; /* Leaf symbol index, or number of leaves before this chain. */
|
39
|
-
char inuse; /* Tracking for garbage collection. */
|
40
40
|
};
|
41
41
|
|
42
42
|
/*
|
43
43
|
Memory pool for nodes.
|
44
44
|
*/
|
45
45
|
typedef struct NodePool {
|
46
|
-
Node*
|
47
|
-
Node* next; /* Pointer to a possibly free node in the pool. */
|
48
|
-
int size; /* Size of the memory pool. */
|
46
|
+
Node* next; /* Pointer to a free node in the pool. */
|
49
47
|
} NodePool;
|
50
48
|
|
51
49
|
/*
|
@@ -55,40 +53,8 @@ static void InitNode(size_t weight, int count, Node* tail, Node* node) {
|
|
55
53
|
node->weight = weight;
|
56
54
|
node->count = count;
|
57
55
|
node->tail = tail;
|
58
|
-
node->inuse = 1;
|
59
56
|
}
|
60
57
|
|
61
|
-
/*
|
62
|
-
Finds a free location in the memory pool. Performs garbage collection if needed.
|
63
|
-
lists: If given, used to mark in-use nodes during garbage collection.
|
64
|
-
maxbits: Size of lists.
|
65
|
-
pool: Memory pool to get free node from.
|
66
|
-
*/
|
67
|
-
static Node* GetFreeNode(Node* (*lists)[2], int maxbits, NodePool* pool) {
|
68
|
-
for (;;) {
|
69
|
-
if (pool->next >= &pool->nodes[pool->size]) {
|
70
|
-
/* Garbage collection. */
|
71
|
-
int i;
|
72
|
-
for (i = 0; i < pool->size; i++) {
|
73
|
-
pool->nodes[i].inuse = 0;
|
74
|
-
}
|
75
|
-
if (lists) {
|
76
|
-
for (i = 0; i < maxbits * 2; i++) {
|
77
|
-
Node* node;
|
78
|
-
for (node = lists[i / 2][i % 2]; node; node = node->tail) {
|
79
|
-
node->inuse = 1;
|
80
|
-
}
|
81
|
-
}
|
82
|
-
}
|
83
|
-
pool->next = &pool->nodes[0];
|
84
|
-
}
|
85
|
-
if (!pool->next->inuse) break; /* Found one. */
|
86
|
-
pool->next++;
|
87
|
-
}
|
88
|
-
return pool->next++;
|
89
|
-
}
|
90
|
-
|
91
|
-
|
92
58
|
/*
|
93
59
|
Performs a Boundary Package-Merge step. Puts a new chain in the given list. The
|
94
60
|
new chain is, depending on the weights, a leaf or a combination of two chains
|
@@ -99,18 +65,16 @@ leaves: The leaves, one per symbol.
|
|
99
65
|
numsymbols: Number of leaves.
|
100
66
|
pool: the node memory pool.
|
101
67
|
index: The index of the list in which a new chain or leaf is required.
|
102
|
-
final: Whether this is the last time this function is called. If it is then it
|
103
|
-
is no more needed to recursively call self.
|
104
68
|
*/
|
105
|
-
static void BoundaryPM(Node* (*lists)[2], int
|
106
|
-
|
69
|
+
static void BoundaryPM(Node* (*lists)[2], Node* leaves, int numsymbols,
|
70
|
+
NodePool* pool, int index) {
|
107
71
|
Node* newchain;
|
108
72
|
Node* oldchain;
|
109
73
|
int lastcount = lists[index][1]->count; /* Count of last chain of list. */
|
110
74
|
|
111
75
|
if (index == 0 && lastcount >= numsymbols) return;
|
112
76
|
|
113
|
-
newchain =
|
77
|
+
newchain = pool->next++;
|
114
78
|
oldchain = lists[index][1];
|
115
79
|
|
116
80
|
/* These are set up before the recursive calls below, so that there is a list
|
@@ -129,15 +93,31 @@ static void BoundaryPM(Node* (*lists)[2], int maxbits,
|
|
129
93
|
newchain);
|
130
94
|
} else {
|
131
95
|
InitNode(sum, lastcount, lists[index - 1][1], newchain);
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
BoundaryPM(lists, maxbits, leaves, numsymbols, pool, index - 1, 0);
|
136
|
-
}
|
96
|
+
/* Two lookahead chains of previous list used up, create new ones. */
|
97
|
+
BoundaryPM(lists, leaves, numsymbols, pool, index - 1);
|
98
|
+
BoundaryPM(lists, leaves, numsymbols, pool, index - 1);
|
137
99
|
}
|
138
100
|
}
|
139
101
|
}
|
140
102
|
|
103
|
+
static void BoundaryPMFinal(Node* (*lists)[2],
|
104
|
+
Node* leaves, int numsymbols, NodePool* pool, int index) {
|
105
|
+
int lastcount = lists[index][1]->count; /* Count of last chain of list. */
|
106
|
+
|
107
|
+
size_t sum = lists[index - 1][0]->weight + lists[index - 1][1]->weight;
|
108
|
+
|
109
|
+
if (lastcount < numsymbols && sum > leaves[lastcount].weight) {
|
110
|
+
Node* newchain = pool->next;
|
111
|
+
Node* oldchain = lists[index][1]->tail;
|
112
|
+
|
113
|
+
lists[index][1] = newchain;
|
114
|
+
newchain->count = lastcount + 1;
|
115
|
+
newchain->tail = oldchain;
|
116
|
+
} else {
|
117
|
+
lists[index][1]->tail = lists[index - 1][1];
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
141
121
|
/*
|
142
122
|
Initializes each list with as lookahead chains the two leaves with lowest
|
143
123
|
weights.
|
@@ -145,8 +125,8 @@ weights.
|
|
145
125
|
static void InitLists(
|
146
126
|
NodePool* pool, const Node* leaves, int maxbits, Node* (*lists)[2]) {
|
147
127
|
int i;
|
148
|
-
Node* node0 =
|
149
|
-
Node* node1 =
|
128
|
+
Node* node0 = pool->next++;
|
129
|
+
Node* node1 = pool->next++;
|
150
130
|
InitNode(leaves[0].weight, 1, 0, node0);
|
151
131
|
InitNode(leaves[1].weight, 2, 0, node1);
|
152
132
|
for (i = 0; i < maxbits; i++) {
|
@@ -161,12 +141,24 @@ last chain of the last list contains the amount of active leaves in each list.
|
|
161
141
|
chain: Chain to extract the bit length from (last chain from last list).
|
162
142
|
*/
|
163
143
|
static void ExtractBitLengths(Node* chain, Node* leaves, unsigned* bitlengths) {
|
144
|
+
int counts[16] = {0};
|
145
|
+
unsigned end = 16;
|
146
|
+
unsigned ptr = 15;
|
147
|
+
unsigned value = 1;
|
164
148
|
Node* node;
|
149
|
+
int val;
|
150
|
+
|
165
151
|
for (node = chain; node; node = node->tail) {
|
166
|
-
|
167
|
-
|
168
|
-
|
152
|
+
counts[--end] = node->count;
|
153
|
+
}
|
154
|
+
|
155
|
+
val = counts[15];
|
156
|
+
while (ptr >= end) {
|
157
|
+
for (; val > counts[ptr - 1]; val--) {
|
158
|
+
bitlengths[leaves[val - 1].count] = value;
|
169
159
|
}
|
160
|
+
ptr--;
|
161
|
+
value++;
|
170
162
|
}
|
171
163
|
}
|
172
164
|
|
@@ -183,6 +175,7 @@ int ZopfliLengthLimitedCodeLengths(
|
|
183
175
|
int i;
|
184
176
|
int numsymbols = 0; /* Amount of symbols with frequency > 0. */
|
185
177
|
int numBoundaryPMRuns;
|
178
|
+
Node* nodes;
|
186
179
|
|
187
180
|
/* Array of lists of chains. Each list requires only two lookahead chains at
|
188
181
|
a time, so each list is a array of two Node*'s. */
|
@@ -219,33 +212,51 @@ int ZopfliLengthLimitedCodeLengths(
|
|
219
212
|
free(leaves);
|
220
213
|
return 0; /* Only one symbol, give it bitlength 1, not 0. OK. */
|
221
214
|
}
|
215
|
+
if (numsymbols == 2) {
|
216
|
+
bitlengths[leaves[0].count]++;
|
217
|
+
bitlengths[leaves[1].count]++;
|
218
|
+
free(leaves);
|
219
|
+
return 0;
|
220
|
+
}
|
222
221
|
|
223
|
-
/* Sort the leaves from lightest to heaviest.
|
222
|
+
/* Sort the leaves from lightest to heaviest. Add count into the same
|
223
|
+
variable for stable sorting. */
|
224
|
+
for (i = 0; i < numsymbols; i++) {
|
225
|
+
if (leaves[i].weight >=
|
226
|
+
((size_t)1 << (sizeof(leaves[0].weight) * CHAR_BIT - 9))) {
|
227
|
+
free(leaves);
|
228
|
+
return 1; /* Error, we need 9 bits for the count. */
|
229
|
+
}
|
230
|
+
leaves[i].weight = (leaves[i].weight << 9) | leaves[i].count;
|
231
|
+
}
|
224
232
|
qsort(leaves, numsymbols, sizeof(Node), LeafComparator);
|
233
|
+
for (i = 0; i < numsymbols; i++) {
|
234
|
+
leaves[i].weight >>= 9;
|
235
|
+
}
|
225
236
|
|
226
|
-
|
227
|
-
|
228
|
-
pool.nodes = (Node*)malloc(pool.size * sizeof(*pool.nodes));
|
229
|
-
pool.next = pool.nodes;
|
230
|
-
for (i = 0; i < pool.size; i++) {
|
231
|
-
pool.nodes[i].inuse = 0;
|
237
|
+
if (numsymbols - 1 < maxbits) {
|
238
|
+
maxbits = numsymbols - 1;
|
232
239
|
}
|
233
240
|
|
241
|
+
/* Initialize node memory pool. */
|
242
|
+
nodes = (Node*)malloc(maxbits * 2 * numsymbols * sizeof(Node));
|
243
|
+
pool.next = nodes;
|
244
|
+
|
234
245
|
lists = (Node* (*)[2])malloc(maxbits * sizeof(*lists));
|
235
246
|
InitLists(&pool, leaves, maxbits, lists);
|
236
247
|
|
237
248
|
/* In the last list, 2 * numsymbols - 2 active chains need to be created. Two
|
238
249
|
are already created in the initialization. Each BoundaryPM run creates one. */
|
239
250
|
numBoundaryPMRuns = 2 * numsymbols - 4;
|
240
|
-
for (i = 0; i < numBoundaryPMRuns; i++) {
|
241
|
-
|
242
|
-
BoundaryPM(lists, maxbits, leaves, numsymbols, &pool, maxbits - 1, final);
|
251
|
+
for (i = 0; i < numBoundaryPMRuns - 1; i++) {
|
252
|
+
BoundaryPM(lists, leaves, numsymbols, &pool, maxbits - 1);
|
243
253
|
}
|
254
|
+
BoundaryPMFinal(lists, leaves, numsymbols, &pool, maxbits - 1);
|
244
255
|
|
245
256
|
ExtractBitLengths(lists[maxbits - 1][1], leaves, bitlengths);
|
246
257
|
|
247
258
|
free(lists);
|
248
259
|
free(leaves);
|
249
|
-
free(
|
260
|
+
free(nodes);
|
250
261
|
return 0; /* OK. */
|
251
262
|
}
|