1 : /* -*- mode: C -*-
2 : *
3 : * File: pdf-stm-f-flate.c
4 : * Date: Tue Jul 10 23:44:00 2007
5 : *
6 : * GNU PDF Library - FlateDecode stream filter
7 : *
8 : */
9 :
10 : /* Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc. */
11 :
12 : /* This program is free software: you can redistribute it and/or modify
13 : * it under the terms of the GNU General Public License as published by
14 : * the Free Software Foundation, either version 3 of the License, or
15 : * (at your option) any later version.
16 : *
17 : * This program is distributed in the hope that it will be useful,
18 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 : * GNU General Public License for more details.
21 : *
22 : * You should have received a copy of the GNU General Public License
23 : * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 : */
25 :
26 : #include <config.h>
27 :
28 : #include <stdio.h>
29 : #include <string.h>
30 :
31 : #include <pdf-alloc.h>
32 : #include <pdf-hash.h>
33 : #include <pdf-stm-f-flate.h>
34 :
35 :
36 : static pdf_status_t read_and_inflate (pdf_stm_f_flate_t st, pdf_buffer_t in,
37 : pdf_buffer_t out);
38 : static pdf_status_t read_and_deflate (pdf_stm_f_flate_t st, pdf_buffer_t in,
39 : pdf_buffer_t out,
40 : pdf_bool_t finish_p);
41 : static int deflate_inbuf (pdf_stm_f_flate_t st, pdf_buffer_t out,
42 : int flush);
43 : static pdf_status_t deflate_inbuf_return (pdf_stm_f_flate_t st,
44 : pdf_buffer_t out,
45 : pdf_bool_t finish_p);
46 :
47 : pdf_status_t
48 : pdf_stm_f_flateenc_init (pdf_hash_t params, void **state)
49 1 : {
50 : pdf_status_t ret;
51 : pdf_stm_f_flate_t filter_state;
52 :
53 1 : filter_state = pdf_alloc (sizeof (struct pdf_stm_f_flate_s));
54 :
55 1 : if (state == NULL)
56 : {
57 0 : ret = PDF_EBADDATA;
58 : }
59 1 : else if (filter_state == NULL)
60 : {
61 0 : ret = PDF_ENOMEM;
62 : }
63 : else
64 : {
65 : /* Initialize fields */
66 1 : filter_state->stream.zalloc = Z_NULL;
67 1 : filter_state->stream.zfree = Z_NULL;
68 1 : filter_state->stream.opaque = Z_NULL;
69 1 : filter_state->writing_p = PDF_FALSE;
70 1 : filter_state->to_write = 0;
71 1 : filter_state->incnt = 0;
72 1 : filter_state->outcnt = 0;
73 1 : filter_state->zret = Z_OK;
74 :
75 1 : if (deflateInit (&(filter_state->stream), Z_DEFAULT_COMPRESSION) != Z_OK)
76 : {
77 0 : ret = PDF_ERROR;
78 : }
79 : else
80 : {
81 1 : *state = (void *) filter_state;
82 1 : ret = PDF_OK;
83 : }
84 : }
85 :
86 1 : return ret;
87 : }
88 :
89 :
90 : pdf_status_t
91 : pdf_stm_f_flatedec_init (pdf_hash_t params, void **state)
92 1 : {
93 : pdf_status_t ret;
94 : pdf_stm_f_flate_t filter_state;
95 :
96 1 : filter_state = pdf_alloc (sizeof (struct pdf_stm_f_flate_s));
97 :
98 1 : if (state == NULL)
99 : {
100 0 : ret = PDF_EBADDATA;
101 : }
102 1 : else if (filter_state == NULL)
103 : {
104 0 : ret = PDF_ENOMEM;
105 : }
106 : else
107 : {
108 : /* Initialize fields */
109 1 : filter_state->stream.zalloc = Z_NULL;
110 1 : filter_state->stream.zfree = Z_NULL;
111 1 : filter_state->stream.opaque = Z_NULL;
112 1 : filter_state->stream.avail_in = 0;
113 1 : filter_state->stream.next_in = Z_NULL;
114 1 : filter_state->writing_p = PDF_FALSE;
115 1 : filter_state->to_write = 0;
116 1 : filter_state->incnt = 0;
117 1 : filter_state->outcnt = 0;
118 1 : filter_state->zret = Z_OK;
119 :
120 1 : if (inflateInit (&(filter_state->stream)) != Z_OK)
121 : {
122 0 : ret = PDF_ERROR;
123 : }
124 : else
125 : {
126 1 : *state = (void *) filter_state;
127 1 : ret = PDF_OK;
128 : }
129 : }
130 :
131 1 : return ret;
132 :
133 : }
134 :
135 :
136 : pdf_status_t
137 : pdf_stm_f_flateenc_apply (pdf_hash_t params, void *state, pdf_buffer_t in,
138 : pdf_buffer_t out, pdf_bool_t finish_p)
139 2160 : {
140 : pdf_stm_f_flate_t st;
141 : pdf_status_t ret;
142 2160 : st = (pdf_stm_f_flate_t) state;
143 :
144 2160 : ret = read_and_deflate (st, in, out, finish_p);
145 4320 : while (ret == PDF_OK)
146 : {
147 0 : ret = read_and_deflate (st, in, out, finish_p);
148 : }
149 2160 : return ret;
150 : }
151 :
152 :
153 : pdf_status_t
154 : pdf_stm_f_flatedec_apply (pdf_hash_t params, void *state, pdf_buffer_t in,
155 : pdf_buffer_t out, pdf_bool_t finish_p)
156 1095 : {
157 : pdf_stm_f_flate_t st;
158 : pdf_status_t ret;
159 1095 : st = (pdf_stm_f_flate_t) state;
160 :
161 1095 : ret = read_and_inflate (st, in, out);
162 2190 : while (ret == PDF_OK)
163 : {
164 0 : ret = read_and_inflate (st, in, out);
165 : }
166 1095 : return ret;
167 : }
168 :
169 :
170 :
171 : pdf_status_t
172 : pdf_stm_f_flatedec_dealloc_state (void *state)
173 1 : {
174 1 : pdf_stm_f_flate_t st = state;
175 1 : inflateEnd(&(st->stream));
176 1 : pdf_dealloc (state);
177 1 : return PDF_OK;
178 : }
179 :
180 : pdf_status_t
181 : pdf_stm_f_flateenc_dealloc_state (void *state)
182 1 : {
183 1 : pdf_dealloc (state);
184 1 : return PDF_OK;
185 : }
186 :
187 :
188 : /* Private functions */
189 :
190 :
191 : static pdf_status_t read_and_deflate (pdf_stm_f_flate_t st, pdf_buffer_t in,
192 : pdf_buffer_t out, pdf_bool_t finish_p)
193 2160 : {
194 :
195 : /* Fill the input CHUNK */
196 2160 : if (!st->writing_p)
197 : {
198 3178 : while (st->incnt < PDF_STM_F_FLATE_CHUNK && !pdf_buffer_eob_p(in))
199 : {
200 1059 : st->inbuf[st->incnt] = in->data[in->rp];
201 1059 : st->incnt++;
202 1059 : in->rp++;
203 : }
204 : /* If more data may come and the input CHUNK has space, ask for it. */
205 2119 : if (!finish_p && st->incnt < PDF_STM_F_FLATE_CHUNK)
206 : {
207 2118 : return PDF_ENINPUT;
208 : }
209 : }
210 :
211 : /*
212 : * Now we have the input CHUNK full or finish_p is set,
213 : * we deflate and write to out.
214 : */
215 84 : return (deflate_inbuf_return (st, out, finish_p));
216 : }
217 :
218 :
219 : static pdf_status_t read_and_inflate (pdf_stm_f_flate_t st, pdf_buffer_t in,
220 : pdf_buffer_t out)
221 1095 : {
222 : /* Fill the input CHUNK */
223 1095 : if (!st->writing_p)
224 : {
225 73 : while (st->incnt < PDF_STM_F_FLATE_CHUNK && !pdf_buffer_eob_p(in))
226 : {
227 36 : st->inbuf[st->incnt] = in->data[in->rp];
228 36 : st->incnt++;
229 36 : in->rp++;
230 : }
231 : }
232 : else
233 : {
234 : /*
235 : * Not nice, but keeps the writing process code clear.
236 : * Notice that the labeled code is inside a while loop,
237 : * so I feel that avoiding this goto won't bring us better code.
238 : */
239 1058 : goto writing;
240 : }
241 :
242 37 : if (st->incnt == 0)
243 : {
244 1 : return PDF_ENINPUT;
245 : }
246 :
247 : /* we inflate and write to out */
248 36 : st->stream.avail_in = st->incnt;
249 36 : st->stream.next_in = st->inbuf;
250 : do {
251 36 : st->stream.avail_out = PDF_STM_F_FLATE_CHUNK;
252 36 : st->stream.next_out = st->outbuf;
253 36 : st->outcnt = 0;
254 :
255 36 : st->zret = inflate(&(st->stream), Z_NO_FLUSH);
256 36 : if (st->zret == Z_STREAM_ERROR || st->zret == Z_NEED_DICT ||
257 : st->zret == Z_DATA_ERROR || st->zret == Z_MEM_ERROR)
258 : {
259 : /* should not be reached */
260 0 : inflateEnd(&(st->stream));
261 0 : return PDF_ERROR;
262 : }
263 :
264 36 : st->to_write = PDF_STM_F_FLATE_CHUNK - st->stream.avail_out;
265 :
266 : writing:
267 2153 : while (st->outcnt < st->to_write && !pdf_buffer_full_p(out))
268 : {
269 1059 : out->data[out->wp] = st->outbuf[st->outcnt];
270 1059 : out->wp++;
271 1059 : st->outcnt++;
272 : }
273 1094 : if (pdf_buffer_full_p(out))
274 : {
275 1059 : st->writing_p = PDF_TRUE;
276 1059 : return PDF_ENOUTPUT;
277 : }
278 35 : } while (st->stream.avail_out == 0);
279 :
280 35 : if (st->zret == Z_STREAM_END)
281 : {
282 0 : return PDF_EEOF;
283 : }
284 : /* the input CHUNK now is empty, if needed, ask for input */
285 35 : st->writing_p = PDF_FALSE;
286 35 : st->incnt = 0;
287 35 : if (pdf_buffer_eob_p(in))
288 : {
289 35 : return PDF_ENINPUT;
290 : }
291 :
292 : /* ask for the input we couldn't read */
293 0 : return PDF_OK;
294 : }
295 :
296 :
297 : static int
298 : deflate_inbuf (pdf_stm_f_flate_t st, pdf_buffer_t out, int flush)
299 42 : {
300 42 : if (st->writing_p)
301 : {
302 : /*
303 : * Not nice, but keeps the writing process code clear.
304 : * Notice that the labeled code is inside a while loop,
305 : * so I feel that avoiding this goto won't bring us better code.
306 : */
307 41 : goto writing;
308 : }
309 :
310 1 : st->stream.avail_in = st->incnt;
311 1 : st->stream.next_in = st->inbuf;
312 : do {
313 1 : st->stream.avail_out = PDF_STM_F_FLATE_CHUNK;
314 1 : st->stream.next_out = st->outbuf;
315 1 : st->outcnt = 0;
316 :
317 1 : st->zret = deflate(&(st->stream), flush);
318 1 : if (st->zret == Z_STREAM_ERROR)
319 : {
320 : /* should not be reached */
321 0 : deflateEnd (&(st->stream));
322 0 : return -1;
323 : }
324 :
325 1 : st->to_write = PDF_STM_F_FLATE_CHUNK - st->stream.avail_out;
326 :
327 : writing:
328 :
329 83 : while (st->outcnt < st->to_write && !pdf_buffer_full_p(out))
330 : {
331 41 : out->data[out->wp++] = st->outbuf[st->outcnt];
332 41 : st->outcnt++;
333 : }
334 42 : if (pdf_buffer_full_p(out))
335 : {
336 41 : st->writing_p = PDF_TRUE;
337 41 : return 1;
338 : }
339 1 : } while (st->stream.avail_out == 0);
340 :
341 1 : st->writing_p = PDF_FALSE;
342 1 : return 0;
343 : }
344 :
345 :
346 : static pdf_status_t
347 : deflate_inbuf_return (pdf_stm_f_flate_t st, pdf_buffer_t out,
348 : pdf_bool_t finish_p)
349 : {
350 : int ret;
351 :
352 42 : if (finish_p)
353 : {
354 42 : ret = deflate_inbuf(st, out, Z_FINISH);
355 : }
356 : else
357 : {
358 0 : ret = deflate_inbuf(st, out, Z_NO_FLUSH);
359 : }
360 :
361 42 : if (ret < 0)
362 : {
363 0 : return PDF_ERROR;
364 : }
365 42 : else if (ret > 0)
366 : {
367 41 : return PDF_ENOUTPUT;
368 : }
369 1 : else if (finish_p)
370 : {
371 1 : deflateEnd (&(st->stream));
372 1 : return PDF_EEOF;
373 : }
374 : else
375 : {
376 : /* the input CHUNK now is empty */
377 0 : st->incnt = 0;
378 : /* ask for the input we couldn't read */
379 0 : return PDF_OK;
380 : }
381 : }
382 :
383 :
384 : /* End of pdf_stm_f_flate.c */
|