1 : /* -*- mode: C -*-
2 : *
3 : * File: pdf-stm.c
4 : * Date: Fri Jul 6 18:43:15 2007
5 : *
6 : * GNU PDF Library - Streams
7 : *
8 : */
9 :
10 : /* Copyright (C) 2007, 2008 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 <unistd.h>
29 : #include <string.h>
30 :
31 : #include <pdf-alloc.h>
32 : #include <pdf-stm.h>
33 :
34 : /* Forward declarations */
35 :
36 : static pdf_status_t pdf_stm_init (pdf_size_t buffer_size,
37 : enum pdf_stm_mode_e mode,
38 : pdf_stm_t stm);
39 : static inline pdf_stm_t pdf_stm_alloc (void);
40 : static inline void pdf_stm_dealloc (pdf_stm_t stm);
41 : static pdf_status_t pdf_stm_read_peek_char (pdf_stm_t stm, pdf_char_t *c, pdf_bool_t peek_p);
42 :
43 : /*
44 : * Public functions
45 : */
46 :
47 : pdf_status_t
48 : pdf_stm_cfile_new (FILE* file,
49 : pdf_off_t offset,
50 : pdf_size_t cache_size,
51 : enum pdf_stm_mode_e mode,
52 : pdf_stm_t *stm)
53 0 : {
54 : /* Allocate memory for the new stream */
55 0 : *stm = pdf_stm_alloc ();
56 :
57 : /* Initialize a file stream */
58 0 : (*stm)->type = PDF_STM_FILE;
59 0 : (*stm)->backend = pdf_stm_be_new_cfile (file,
60 : offset);
61 :
62 : /* Initialize the common parts */
63 0 : return pdf_stm_init (cache_size,
64 : mode,
65 : *stm);
66 : }
67 :
68 :
69 : pdf_status_t
70 : pdf_stm_file_new (pdf_fsys_file_t file,
71 : pdf_off_t offset,
72 : pdf_size_t cache_size,
73 : enum pdf_stm_mode_e mode,
74 : pdf_stm_t *stm)
75 10 : {
76 : /* Allocate memory for the new stream */
77 10 : *stm = pdf_stm_alloc ();
78 :
79 : /* Initialize a file stream */
80 10 : (*stm)->type = PDF_STM_FILE;
81 10 : (*stm)->backend = pdf_stm_be_new_file (file,
82 : offset);
83 :
84 : /* Initialize the common parts */
85 10 : return pdf_stm_init (cache_size,
86 : mode,
87 : *stm);
88 : }
89 :
90 : pdf_status_t
91 : pdf_stm_mem_new (pdf_char_t *buffer,
92 : pdf_size_t size,
93 : pdf_size_t cache_size,
94 : enum pdf_stm_mode_e mode,
95 : pdf_stm_t *stm)
96 792 : {
97 : /* Allocate memory for the new stream */
98 792 : *stm = pdf_stm_alloc ();
99 :
100 : /* Initialize a memory stream */
101 792 : (*stm)->type = PDF_STM_MEM;
102 792 : (*stm)->backend = pdf_stm_be_new_mem (buffer,
103 : size,
104 : 0);
105 :
106 : /* Initialize the common parts */
107 792 : return pdf_stm_init (cache_size,
108 : mode,
109 : *stm);
110 : }
111 :
112 : pdf_status_t
113 : pdf_stm_destroy (pdf_stm_t stm)
114 617 : {
115 : pdf_stm_filter_t filter;
116 : pdf_stm_filter_t filter_to_delete;
117 : pdf_size_t flushed_bytes;
118 : pdf_status_t ret;
119 :
120 617 : ret = PDF_OK;
121 617 : if (stm->mode == PDF_STM_WRITE)
122 : {
123 : /* Flush the cache, finishing the filters */
124 40 : ret = pdf_stm_flush (stm, PDF_TRUE, &flushed_bytes);
125 : }
126 :
127 : /* Destroy the backend */
128 617 : pdf_stm_be_destroy (stm->backend);
129 :
130 : /* Destroy the cache */
131 617 : pdf_buffer_destroy (stm->cache);
132 :
133 : /* Destroy the filter chain */
134 617 : filter = stm->filter;
135 1907 : while (filter != NULL)
136 : {
137 673 : filter_to_delete = filter;
138 673 : filter = filter->next;
139 673 : pdf_stm_filter_destroy (filter_to_delete);
140 : }
141 :
142 : /* Deallocate the stm structure */
143 : pdf_stm_dealloc (stm);
144 :
145 617 : return ret;
146 : }
147 :
148 : enum pdf_stm_mode_e
149 : pdf_stm_get_mode (pdf_stm_t stm)
150 2 : {
151 2 : return stm->mode;
152 : }
153 :
154 : pdf_status_t
155 : pdf_stm_read (pdf_stm_t stm,
156 : pdf_char_t *buf,
157 : pdf_size_t bytes,
158 : pdf_size_t *read_bytes)
159 48 : {
160 : pdf_size_t pending_bytes;
161 : pdf_size_t cache_size;
162 : pdf_size_t to_copy_bytes;
163 : pdf_status_t ret;
164 :
165 48 : if (stm->mode != PDF_STM_READ)
166 : {
167 : /* Invalid operation */
168 0 : return PDF_EINVOP;
169 : }
170 :
171 48 : ret = PDF_OK;
172 48 : *read_bytes = 0;
173 1247 : while ((*read_bytes < bytes) &&
174 : (ret == PDF_OK))
175 : {
176 : /* If the cache is empty, refill it with filtered data */
177 1151 : if (pdf_buffer_eob_p (stm->cache))
178 : {
179 1146 : pdf_buffer_rewind (stm->cache);
180 1146 : ret = pdf_stm_filter_apply (stm->filter, PDF_FALSE);
181 : }
182 :
183 1151 : if (ret != PDF_ERROR)
184 : {
185 : /* Read data from the cache */
186 1137 : pending_bytes = bytes - *read_bytes;
187 1137 : cache_size = stm->cache->wp - stm->cache->rp;
188 1137 : to_copy_bytes = PDF_MIN(pending_bytes, cache_size);
189 :
190 1137 : memcpy ((char *) (buf + *read_bytes),
191 : (char *) stm->cache->data + stm->cache->rp,
192 : to_copy_bytes);
193 :
194 1137 : *read_bytes += to_copy_bytes;
195 1137 : stm->cache->rp += to_copy_bytes;
196 : }
197 : }
198 :
199 48 : if ((*read_bytes == bytes) &&
200 : (ret == PDF_EEOF))
201 : {
202 : /* Avoid a false PDF_EEOF in the current operation */
203 25 : ret = PDF_OK;
204 : }
205 :
206 : /* Update the sequential counter */
207 48 : stm->seq_counter += *read_bytes;
208 :
209 48 : return ret;
210 : }
211 :
212 :
213 : pdf_status_t
214 : pdf_stm_write (pdf_stm_t stm,
215 : const pdf_char_t *buf,
216 : pdf_size_t bytes,
217 : pdf_size_t *written_bytes)
218 27 : {
219 : pdf_status_t ret;
220 : pdf_size_t pending_bytes;
221 : pdf_size_t to_write_bytes;
222 : pdf_stm_filter_t tail_filter;
223 : pdf_buffer_t tail_buffer;
224 : pdf_size_t tail_buffer_size;
225 : pdf_size_t flushed_bytes;
226 :
227 27 : if (stm->mode != PDF_STM_WRITE)
228 : {
229 : /* Invalid operation */
230 0 : return PDF_EINVOP;
231 : }
232 :
233 27 : tail_filter = pdf_stm_filter_get_tail (stm->filter);
234 27 : tail_buffer = pdf_stm_filter_get_in (tail_filter);
235 :
236 27 : ret = PDF_OK;
237 27 : *written_bytes = 0;
238 1228 : while ((*written_bytes < bytes) &&
239 : (ret == PDF_OK))
240 : {
241 1174 : if ((pdf_buffer_full_p (tail_buffer)) &&
242 : (!pdf_buffer_eob_p (tail_buffer)))
243 : {
244 : /* Flush the cache */
245 1147 : tail_buffer_size = tail_buffer->wp - tail_buffer->rp;
246 :
247 1147 : if (pdf_stm_flush (stm, PDF_FALSE, &flushed_bytes)
248 : == PDF_ERROR)
249 : {
250 0 : ret = PDF_ERROR;
251 : }
252 1147 : else if (flushed_bytes < tail_buffer_size)
253 : {
254 0 : ret = PDF_EEOF;
255 : }
256 : }
257 :
258 1174 : if (ret == PDF_OK)
259 : {
260 : /* Write the data into the tail buffer. Note that at this
261 : point the tail buffer should be empty */
262 1174 : tail_buffer_size = tail_buffer->size - tail_buffer->wp;
263 1174 : pending_bytes = bytes - *written_bytes;
264 :
265 1174 : to_write_bytes = PDF_MIN(pending_bytes, tail_buffer_size);
266 :
267 1174 : if (to_write_bytes != 0)
268 : {
269 1174 : memcpy ((char *) tail_buffer->data + tail_buffer->wp,
270 : (char *) buf + *written_bytes,
271 : to_write_bytes);
272 :
273 1174 : *written_bytes += to_write_bytes;
274 1174 : tail_buffer->wp += to_write_bytes;
275 : }
276 : }
277 : }
278 :
279 27 : if ((*written_bytes == bytes) &&
280 : (ret == PDF_EEOF))
281 : {
282 : /* Avoid a false PDF_EEOF in the current operation */
283 0 : ret = PDF_OK;
284 : }
285 :
286 : /* Update the sequential counter */
287 27 : stm->seq_counter += *written_bytes;
288 :
289 27 : return ret;
290 : }
291 :
292 : pdf_status_t
293 : pdf_stm_flush (pdf_stm_t stm,
294 : pdf_bool_t finish_p,
295 : pdf_size_t *flushed_bytes)
296 1207 : {
297 : pdf_stm_filter_t tail_filter;
298 : pdf_buffer_t tail_buffer;
299 : pdf_status_t ret;
300 : pdf_size_t cache_size;
301 : pdf_size_t written_bytes;
302 : pdf_size_t tail_size;
303 :
304 1207 : if (stm->mode != PDF_STM_WRITE)
305 : {
306 : /* Invalid operation */
307 2 : return 0;
308 : }
309 :
310 : /* Apply the head filter until the filter chain gets empty */
311 1205 : tail_filter = pdf_stm_filter_get_tail (stm->filter);
312 1205 : tail_buffer = pdf_stm_filter_get_in (tail_filter);
313 1205 : *flushed_bytes = 0;
314 : while (1)
315 : {
316 1342 : tail_size = tail_buffer->wp - tail_buffer->rp;
317 1342 : ret = pdf_stm_filter_apply (stm->filter, finish_p);
318 :
319 1342 : if (ret == PDF_ERROR)
320 : {
321 : /* The filter chain is in error */
322 0 : break;
323 : }
324 :
325 : /* Update the number of flushed bytes */
326 1342 : *flushed_bytes += tail_size
327 : - (tail_buffer->wp - tail_buffer->rp);
328 :
329 1342 : if ((ret == PDF_EEOF)
330 : && (pdf_buffer_eob_p (stm->cache)))
331 : {
332 1202 : pdf_buffer_rewind (tail_buffer);
333 1202 : ret = PDF_OK;
334 1202 : break;
335 : }
336 :
337 : /* Write the data from the buffer cache into the backend */
338 140 : cache_size = stm->cache->wp - stm->cache->rp;
339 140 : written_bytes = pdf_stm_be_write (stm->backend,
340 : stm->cache->data + stm->cache->rp,
341 : cache_size);
342 :
343 140 : if (written_bytes != cache_size)
344 : {
345 : /* Could not write all the contents of the cache buffer into
346 : the backend */
347 3 : stm->cache->rp += written_bytes;
348 3 : ret = PDF_EEOF;
349 3 : break;
350 : }
351 :
352 : /* Rewind the cache */
353 137 : pdf_buffer_rewind (stm->cache);
354 137 : }
355 :
356 1205 : return ret;
357 : }
358 :
359 : pdf_status_t
360 : pdf_stm_install_filter (pdf_stm_t stm,
361 : enum pdf_stm_filter_type_e filter_type,
362 : pdf_hash_t filter_params)
363 57 : {
364 : pdf_status_t ret;
365 : pdf_stm_filter_t filter;
366 : enum pdf_stm_filter_mode_e filter_mode;
367 :
368 57 : if (stm->mode == PDF_STM_READ)
369 : {
370 39 : filter_mode = PDF_STM_FILTER_MODE_READ;
371 : }
372 : else
373 : {
374 18 : filter_mode = PDF_STM_FILTER_MODE_WRITE;
375 : }
376 :
377 : /* Create the new filter */
378 57 : ret = pdf_stm_filter_new (filter_type,
379 : filter_params,
380 : stm->cache->size,
381 : filter_mode,
382 : &filter);
383 :
384 57 : if (filter != NULL)
385 : {
386 : /* Set the new filter as the new head of the filter chain */
387 56 : pdf_stm_filter_set_next (filter, stm->filter);
388 56 : pdf_stm_filter_set_out (filter, stm->cache);
389 56 : pdf_stm_filter_set_out (stm->filter, pdf_stm_filter_get_in (filter));
390 56 : stm->filter = filter;
391 : }
392 :
393 57 : return ret;
394 : }
395 :
396 : pdf_status_t
397 : pdf_stm_read_char (pdf_stm_t stm, pdf_char_t *c)
398 89675 : {
399 89675 : return pdf_stm_read_peek_char (stm, c, PDF_FALSE);
400 : }
401 :
402 : pdf_status_t
403 : pdf_stm_peek_char (pdf_stm_t stm, pdf_char_t *c)
404 49615 : {
405 49615 : return pdf_stm_read_peek_char (stm, c, PDF_TRUE);
406 : }
407 :
408 : pdf_off_t
409 : pdf_stm_bseek (pdf_stm_t stm,
410 : pdf_off_t pos)
411 7 : {
412 : pdf_off_t cur_pos;
413 : pdf_off_t new_pos;
414 : pdf_stm_filter_t tail_filter;
415 : pdf_buffer_t tail_buffer;
416 : pdf_size_t flushed_bytes;
417 :
418 7 : cur_pos = pdf_stm_tell (stm);
419 :
420 7 : if (stm->mode == PDF_STM_READ)
421 : {
422 : /* Discard the cache contents */
423 6 : pdf_buffer_rewind (stm->cache);
424 :
425 : /* Seek the backend */
426 6 : new_pos = pdf_stm_be_seek (stm->backend, pos);
427 : }
428 : else
429 : {
430 : /* Writing stream */
431 :
432 1 : tail_filter = pdf_stm_filter_get_tail (stm->filter);
433 1 : tail_buffer = pdf_stm_filter_get_in (tail_filter);
434 1 : if (!pdf_buffer_eob_p (tail_buffer))
435 : {
436 : /* Flush the stream */
437 1 : if (pdf_stm_flush (stm, PDF_FALSE, &flushed_bytes)
438 : == PDF_ERROR)
439 : {
440 : /* Error flushing the stream: return the current
441 : position */
442 0 : return cur_pos;
443 : }
444 : }
445 :
446 : /* Note that if there is an EOF condition in the backend we are
447 : going to loose data */
448 1 : pdf_buffer_rewind (tail_buffer);
449 :
450 : /* Seek the backend */
451 1 : new_pos = pdf_stm_be_seek (stm->backend, pos);
452 : }
453 :
454 : /* Reset the sequential counter */
455 7 : stm->seq_counter = 0;
456 :
457 7 : return new_pos;
458 : }
459 :
460 : pdf_off_t
461 : pdf_stm_btell (pdf_stm_t stm)
462 2 : {
463 : pdf_off_t pos;
464 : pdf_size_t cache_size;
465 : pdf_stm_filter_t tail_filter;
466 : pdf_buffer_t tail_buffer;
467 :
468 2 : if (stm->mode == PDF_STM_READ)
469 : {
470 2 : cache_size = stm->cache->wp - stm->cache->rp;
471 2 : pos = pdf_stm_be_tell (stm->backend) - cache_size;
472 : }
473 : else
474 : {
475 : /* Writing stream */
476 :
477 0 : tail_filter = pdf_stm_filter_get_tail (stm->filter);
478 0 : tail_buffer = pdf_stm_filter_get_in (tail_filter);
479 :
480 0 : cache_size = tail_buffer->wp - tail_buffer->rp;
481 0 : pos = pdf_stm_be_tell (stm->backend) - cache_size;
482 : }
483 :
484 2 : return pos;
485 : }
486 :
487 : pdf_off_t
488 : pdf_stm_tell (pdf_stm_t stm)
489 2493 : {
490 2493 : return stm->seq_counter;
491 : }
492 :
493 : /*
494 : * Private functions
495 : */
496 :
497 : static pdf_status_t
498 : pdf_stm_init (pdf_size_t cache_size,
499 : enum pdf_stm_mode_e mode,
500 : pdf_stm_t stm)
501 802 : {
502 : pdf_hash_t null_filter_params;
503 : enum pdf_stm_filter_mode_e filter_mode;
504 : pdf_status_t ret;
505 :
506 802 : if (cache_size == 0)
507 : {
508 : /* Use the default cache size */
509 592 : cache_size = PDF_STM_DEFAULT_CACHE_SIZE;
510 : }
511 :
512 : /* The sequential counter is initially 0 */
513 802 : stm->seq_counter = 0;
514 :
515 : /* Initialize the null filter */
516 802 : pdf_hash_new (NULL, &null_filter_params);
517 :
518 802 : if (stm->mode == PDF_STM_READ)
519 : {
520 694 : filter_mode = PDF_STM_FILTER_MODE_READ;
521 : }
522 : else
523 : {
524 108 : filter_mode = PDF_STM_FILTER_MODE_WRITE;
525 : }
526 :
527 802 : ret = pdf_stm_filter_new (PDF_STM_FILTER_NULL,
528 : null_filter_params,
529 : cache_size,
530 : filter_mode,
531 : &(stm->filter));
532 :
533 802 : if (ret == PDF_OK)
534 : {
535 : /* Initialize the filter cache */
536 802 : stm->cache = pdf_buffer_new (cache_size);
537 :
538 : /* Configure the filter */
539 802 : stm->mode = mode;
540 802 : if (stm->mode == PDF_STM_READ)
541 : {
542 : /* Configuration for a reading stream
543 : *
544 : * <cache> <--- <null-filter> <--- <backend>
545 : */
546 :
547 762 : pdf_stm_filter_set_out (stm->filter,
548 : stm->cache);
549 762 : pdf_stm_filter_set_be (stm->filter,
550 : stm->backend);
551 : }
552 : else
553 : {
554 : /* Configuration for a writing stream
555 : *
556 : * <null-filter> --> <cache> --> <backend>
557 : */
558 :
559 40 : pdf_stm_filter_set_out (stm->filter,
560 : stm->cache);
561 : }
562 : }
563 :
564 802 : return ret;
565 : }
566 :
567 :
568 : static pdf_status_t
569 : pdf_stm_read_peek_char (pdf_stm_t stm,
570 : pdf_char_t *c,
571 : pdf_bool_t peek_p)
572 139290 : {
573 : pdf_status_t ret;
574 :
575 : /* Is this a read stream? */
576 139290 : if (stm->mode != PDF_STM_READ)
577 : {
578 : /* Invalid operation */
579 0 : return PDF_EINVOP;
580 : }
581 :
582 : /* Is the cache empty? */
583 139290 : ret = PDF_OK;
584 139290 : if (pdf_buffer_eob_p (stm->cache))
585 : {
586 1095 : pdf_buffer_rewind (stm->cache);
587 1095 : ret = pdf_stm_filter_apply (stm->filter, PDF_FALSE);
588 : }
589 :
590 139290 : if (pdf_buffer_eob_p (stm->cache))
591 : {
592 361 : ret = PDF_EEOF;
593 : }
594 : else
595 : {
596 : /* Read a character from the cache */
597 138929 : *c =
598 : (pdf_u32_t) stm->cache->data[stm->cache->rp];
599 :
600 138929 : if (!peek_p)
601 : {
602 89672 : stm->cache->rp++;
603 : }
604 :
605 138929 : if (ret == PDF_EEOF)
606 : {
607 : /* Avoid a false PDF_EEOF */
608 599 : ret = PDF_OK;
609 : }
610 : }
611 :
612 139290 : if ((!peek_p) && (ret == PDF_OK))
613 : {
614 : /* Update the sequential counter */
615 89672 : stm->seq_counter++;
616 : }
617 :
618 139290 : return ret;
619 : }
620 :
621 : static inline pdf_stm_t
622 : pdf_stm_alloc (void)
623 : {
624 : pdf_stm_t new;
625 :
626 802 : new = pdf_alloc (sizeof(struct pdf_stm_s));
627 802 : return new;
628 : }
629 :
630 : static inline void
631 : pdf_stm_dealloc (pdf_stm_t stm)
632 : {
633 617 : pdf_dealloc (stm);
634 : }
635 :
636 : /* End of pdf_stm.c */
|