/src/ghostpdl/base/slzwe.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* LZW encoding filter */ |
18 | | #include "stdio_.h" /* includes std.h */ |
19 | | #include "gdebug.h" |
20 | | #include "strimpl.h" |
21 | | #include "slzwx.h" |
22 | | |
23 | | /********************************************************/ |
24 | | /* LZW routines are based on: */ |
25 | | /* Dr. Dobbs Journal --- Oct. 1989. */ |
26 | | /* Article on LZW Data Compression by Mark R. Nelson */ |
27 | | /********************************************************/ |
28 | | |
29 | | /* Define the special codes */ |
30 | 604k | #define code_reset 256 |
31 | 17.0G | #define code_eod 257 |
32 | 302k | #define code_0 258 /* first assignable code */ |
33 | | |
34 | | /* Import the stream closing procedure */ |
35 | | extern stream_proc_release(s_LZW_release); |
36 | | |
37 | | typedef struct lzw_encode_s { |
38 | | byte datum; /* last byte of this code */ |
39 | | ushort prefix; /* code for prefix of this code */ |
40 | | } lzw_encode; |
41 | | |
42 | 159G | #define encode_max 4095 /* max # of codes, must be */ |
43 | | /* > code_0 and <= 4095 */ |
44 | 79.8G | #define hash_size (encode_max + encode_max / 4) |
45 | | |
46 | | struct lzw_encode_table_s { |
47 | | lzw_encode encode[encode_max]; |
48 | | ushort hashed[hash_size]; |
49 | | }; |
50 | | gs_private_st_simple(st_lzwe_table, lzw_encode_table, "lzw_encode_table"); |
51 | | |
52 | | /* Hashing function */ |
53 | | #define encode_hash(code, chr)\ |
54 | 39.0G | ((uint)((code) * 59 + (chr) * ((hash_size / 256) | 1)) % hash_size) |
55 | | |
56 | | /* Internal routine to put a code into the output buffer. */ |
57 | | /* Let S = ss->code_size, M = ss->next_code, N = 1 << M. */ |
58 | | /* Relevant invariants: 9 <= S <= 12; N / 2 <= M < N; 0 <= code < N; */ |
59 | | /* 1 <= ss->bits_left <= 8; only the rightmost (8 - ss->bits_left) */ |
60 | | /* bits of ss->bits contain valid data. */ |
61 | | static byte * |
62 | | lzw_put_code(register stream_LZW_state *ss, byte *q, uint code) |
63 | 832M | { uint size = ss->code_size; |
64 | 832M | byte cb = (ss->bits << ss->bits_left) + |
65 | 832M | (code >> (size - ss->bits_left)); |
66 | 832M | if_debug2m('W', ss->memory, "[w]writing 0x%x,%d\n", code, ss->code_size); |
67 | 832M | *++q = cb; |
68 | 832M | if ( (ss->bits_left += 8 - size) <= 0 ) |
69 | 335M | { *++q = code >> -ss->bits_left; |
70 | 335M | ss->bits_left += 8; |
71 | 335M | } |
72 | 832M | ss->bits = code; |
73 | 832M | return q; |
74 | 832M | } |
75 | | |
76 | | /* Internal routine to reset the encoding table */ |
77 | | static void |
78 | | lzw_reset_encode(stream_LZW_state *ss) |
79 | 302k | { register int c; |
80 | 302k | lzw_encode_table *table = ss->table.encode; |
81 | 302k | ss->next_code = code_0; |
82 | 302k | ss->code_size = 9; |
83 | 302k | ss->prev_code = code_eod; |
84 | 1.54G | for ( c = 0; c < hash_size; c++ ) |
85 | 1.54G | table->hashed[c] = code_eod; |
86 | 77.6M | for ( c = 0; c < 256; c++ ) |
87 | 77.3M | { lzw_encode *ec = &table->encode[c]; |
88 | 77.3M | register ushort *tc = &table->hashed[encode_hash(code_eod, c)]; |
89 | 77.3M | while ( *tc != code_eod ) |
90 | 0 | if ( ++tc == &table->hashed[hash_size] ) |
91 | 0 | tc = &table->hashed[0]; |
92 | 77.3M | *tc = c; |
93 | 77.3M | ec->datum = c, ec->prefix = code_eod; |
94 | 77.3M | } |
95 | 302k | table->encode[code_eod].prefix = code_reset; /* guarantee no match */ |
96 | 302k | } |
97 | | |
98 | 3.45G | #define ss ((stream_LZW_state *)st) |
99 | | |
100 | | /* Initialize LZWEncode filter */ |
101 | | static int |
102 | | s_LZWE_init(stream_state *st) |
103 | 98.0k | { ss->bits_left = 8; |
104 | 98.0k | ss->bits = 0; /* for Purify, the value unimportant due to ss->bits_left == 8 */ |
105 | 98.0k | ss->table.encode = gs_alloc_struct(st->memory, |
106 | 98.0k | lzw_encode_table, &st_lzwe_table, "LZWEncode init"); |
107 | 98.0k | if ( ss->table.encode == 0 ) |
108 | 0 | return ERRC; /****** WRONG ******/ |
109 | 98.0k | ss->first = true; |
110 | 98.0k | lzw_reset_encode(ss); |
111 | 98.0k | return 0; |
112 | 98.0k | } |
113 | | |
114 | | /* Process a buffer */ |
115 | | static int |
116 | | s_LZWE_process(stream_state *st, stream_cursor_read *pr, |
117 | | stream_cursor_write *pw, bool last) |
118 | 159M | { register const byte *p = pr->ptr; |
119 | 159M | const byte *rlimit = pr->limit; |
120 | 159M | register byte *q = pw->ptr; |
121 | 159M | byte *wlimit = pw->limit; |
122 | 159M | int code = ss->prev_code; |
123 | 159M | lzw_encode_table *table = ss->table.encode; |
124 | 159M | ushort *table_end = &table->hashed[hash_size]; |
125 | 159M | int status = 0; |
126 | 159M | int limit_code; |
127 | 159M | #define set_limit_code()\ |
128 | 160M | limit_code = (1 << ss->code_size) - ss->EarlyChange;\ |
129 | 160M | if ( limit_code > encode_max ) limit_code = encode_max |
130 | 159M | set_limit_code(); |
131 | 159M | if ( ss->first ) |
132 | 98.0k | { /* Emit the initial reset code. */ |
133 | 98.0k | if ( wlimit - q < 2 ) |
134 | 0 | return 1; |
135 | 98.0k | q = lzw_put_code(ss, q, code_reset); |
136 | 98.0k | ss->first = false; |
137 | 98.0k | } |
138 | 39.1G | while ( p < rlimit ) |
139 | 39.0G | { byte c = p[1]; |
140 | 39.0G | ushort *tp; |
141 | 39.0G | for ( tp = &table->hashed[encode_hash(code, c)]; ; ) |
142 | 52.6G | { lzw_encode *ep = &table->encode[*tp]; |
143 | 52.6G | if ( ep->prefix == code && ep->datum == c ) |
144 | 38.1G | { code = *tp; |
145 | 38.1G | p++; |
146 | 38.1G | break; |
147 | 38.1G | } |
148 | 14.4G | else if ( *tp != code_eod ) |
149 | 13.6G | { if ( ++tp == table_end ) |
150 | 8.01M | tp = &table->hashed[0]; /* wrap around */ |
151 | 13.6G | } |
152 | 841M | else |
153 | 841M | { /* end of recognized sequence */ |
154 | 841M | if ( wlimit - q <= 4 ) |
155 | 9.20M | { status = 1; |
156 | 9.20M | goto out; |
157 | 9.20M | } |
158 | 831M | q = lzw_put_code(ss, q, code); |
159 | 831M | if ( ss->next_code == limit_code ) |
160 | 877k | { /* Reached power of 2 or limit. */ |
161 | | /* Determine which. */ |
162 | 877k | if ( ss->next_code == encode_max ) |
163 | 204k | { q = lzw_put_code(ss, q, code_reset); |
164 | 204k | lzw_reset_encode(ss); |
165 | 204k | set_limit_code(); |
166 | 204k | goto cx; |
167 | 204k | } |
168 | 672k | ss->code_size++; |
169 | 672k | set_limit_code(); |
170 | 672k | } |
171 | 831M | if_debug3m('W', ss->memory, "[W]encoding 0x%x=0x%x+%c\n", |
172 | 831M | ss->next_code, code, c); |
173 | 831M | *tp = ss->next_code++; |
174 | 831M | ep = &table->encode[*tp]; |
175 | 831M | ep->datum = c; |
176 | 831M | ep->prefix = code; |
177 | 831M | cx: code = code_eod; |
178 | 831M | break; |
179 | 831M | } |
180 | 52.6G | } |
181 | 39.0G | } |
182 | 150M | if ( last && status == 0 ) |
183 | 98.0k | { if ( wlimit - q < 4 ) |
184 | 72 | status = 1; |
185 | 98.0k | else |
186 | 98.0k | { if ( code != code_eod ) |
187 | 96.4k | { q = lzw_put_code(ss, q, code); /* put out final code */ |
188 | 96.4k | if (ss->next_code == limit_code && ss->next_code != encode_max) |
189 | 84 | ss->code_size++; |
190 | 96.4k | } |
191 | 98.0k | q = lzw_put_code(ss, q, code_eod); |
192 | 98.0k | if ( ss->bits_left < 8 ) |
193 | 95.4k | *++q = ss->bits << ss->bits_left; /* final byte */ |
194 | 98.0k | } |
195 | 98.0k | } |
196 | 159M | out: ss->prev_code = code; |
197 | 159M | pr->ptr = p; |
198 | 159M | pw->ptr = q; |
199 | 159M | return status; |
200 | 150M | } |
201 | | |
202 | | #undef ss |
203 | | |
204 | | /* Stream template */ |
205 | | const stream_template s_LZWE_template = |
206 | | { &st_LZW_state, s_LZWE_init, s_LZWE_process, 1, 4, s_LZW_release, |
207 | | s_LZW_set_defaults |
208 | | }; |