1 /******************************************************************************
2 File: $Id: pclcomp.c,v 1.11 2000/10/07 17:51:57 Martin Rel $
3 Contents: Implementation of PCL compression routines
4 Author: Martin Lottermoser, Greifswaldstrasse 28, 38124 Braunschweig,
5 Germany. E-mail: Martin.Lottermoser@t-online.de.
6
7 *******************************************************************************
8 * *
9 * Copyright (C) 1996, 1997, 1998, 2000 by Martin Lottermoser *
10 * All rights reserved *
11 * *
12 *******************************************************************************
13
14 If you compile with NDEBUG defined, some runtime checks for programming
15 errors (mine and the interface's user's) are omitted.
16
17 ******************************************************************************/
18
19 /* Configuration management identification */
20 #ifndef lint
21 static const char
22 cm_id[] = "@(#)$Id: pclcomp.c,v 1.11 2000/10/07 17:51:57 Martin Rel $";
23 #endif
24
25 /*****************************************************************************/
26
27 #ifndef _XOPEN_SOURCE
28 #define _XOPEN_SOURCE 500
29 #endif
30
31 /* Interface definition */
32 #include "pclgen.h"
33
34 /* Standard headers */
35 #include <assert.h>
36 #include <string.h>
37
38 /*****************************************************************************/
39
40 /* For TIFF compression, we need the two's complement representation of
41 numbers in the range [-127, -1], expressed in a 'pcl_Octet'.
42
43 The macro neg() must accept as an argument an 'int' expression with a value
44 in that range and convert it such that the result can be assigned to a
45 variable of type 'pcl_Octet' with the result that the value of the latter
46 becomes the two's complement representation of the number with respect to
47 256.
48
49 On most machines one can use a simple assignment. However, the C standard
50 specifies the behaviour in these cases (signed to unsigned where the source
51 has more bits than the target and the source value is negative) to be
52 implementation-defined. Hence the need for a portable solution.
53 Define PCLCOMP_NEG if you don't like it.
54 */
55
56 #ifdef PCLCOMP_NEG
57 #define neg(number) (number)
58 #else
59 #define neg(number) (256 + (number))
60 #endif
61
62 /******************************************************************************
63
64 Function: compress_runlength
65
66 This function performs runlength encoding.
67
68 Runlength encoding consists in preceding each octet by a repeat count octet
69 the value of which is one less than the repeat count.
70
71 'in' and 'incount' describe the row to be compressed, 'out' an area of at
72 least 'maxoutcount' octets to which the result is to be written. The function
73 returns the number of octets written or a negative value on error.
74
75 'incount' may be zero, in which case the values of the other arguments are
76 irrelevant. Otherwise, 'in' must be non-NULL, and if 'maxoutcount' is positive
77 'out' must be non-NULL as well.
78
79 ******************************************************************************/
80
compress_runlength(const pcl_Octet * in,int incount,pcl_Octet * out,int maxoutcount)81 static int compress_runlength(const pcl_Octet *in, int incount, pcl_Octet *out,
82 int maxoutcount)
83 {
84 int available = maxoutcount;
85
86 /* Here 'incount' is the number of octets to be encoded and 'in' points to
87 the first of them. 'available' is the number of octets available in the
88 output array and 'out' points to the first of them. */
89 while (incount > 0 && available > 1) {
90 int count = 0;
91
92 out++; /* skip repetition count octet, to be filled in later */
93 *out = *in;
94 do {
95 in++; incount--; count++;
96 } while (incount > 0 && *in == *out && count < 256);
97 *(out - 1) = count - 1;
98 out++; available -= 2;
99 }
100
101 if (incount > 0) return -1;
102 return maxoutcount - available;
103 }
104
105 /******************************************************************************
106
107 Function: compress_tiff
108
109 This function performs TIFF compression (compression method 2).
110
111 'in' and 'incount' describe the row to be compressed, 'out' an area of at
112 least 'maxoutcount' octets to which the result is to be written. The function
113 returns the number of octets written or a negative value on error.
114
115 'in' must be non-NULL, 'incount' must be positive, 'maxoutcount' must be
116 non-negative, and 'out' must be non-NULL is 'maxoutcount' is positive.
117
118 TIFF compression creates an octet stream consisting of three kinds of
119 octet sequences:
120 - an octet with value in the range [-127, -1] (two's complement)
121 followed by a single octet: this means the second octet is to be
122 repeated -<first octet>+1 times,
123 - an octet with value in the range [0, 127]: this means the next
124 <first octet>+1 octets have not been compressed,
125 - an octet with the value -128: this is a no-op and must be ignored.
126 The first octet determining the kind of sequence is called the "control
127 byte".
128
129 This routine generates an output string with a length which is minimal
130 for TIFF compression (if it doesn't, it's a bug). Readability of the code
131 and minimal execution speed were secondary considerations.
132
133 I have implemented this as a finite state machine. As can be seen from
134 the code, I sacrificed the rules of structured programming for this,
135 because I found a state transition diagram much more intelligible
136 than anything I could code. I then simply transferred it into C.
137
138 ******************************************************************************/
139
compress_tiff(const pcl_Octet * in,int incount,pcl_Octet * out,int maxoutcount)140 static int compress_tiff(const pcl_Octet *in, int incount, pcl_Octet *out,
141 int maxoutcount)
142 {
143 pcl_Octet
144 last; /* a remembered octet before the current 'in' value */
145 const pcl_Octet
146 *end = in + incount - 1; /* last position in 'in' */
147 int
148 available = maxoutcount, /* number of free octets left in 'out' */
149 repeated, /* repeat count during a compressed sequence */
150 stored; /* store count during a non-compressed sequence */
151
152 state1:
153 /* No octet is held over to be treated, 'in' points to the next one */
154 if (in == end) {
155 /* This is the last octet and a single one. */
156 if (available < 2) return -1;
157 *out = 0; out++; /* control byte */
158 *out = *in;
159 available -= 2;
160 goto finished;
161 }
162 last = *in; in++; /* Fetch one octet and remember it. */
163 /* to state2 */
164
165 state2:
166 /* One octet to be treated is in 'last', 'in' points to the next. */
167 if (*in != last) {
168 if (available < 3) return -1;
169 out++; available--; /* Skip control byte to be filled in later */
170 stored = 0;
171 goto state4;
172 }
173 if (available < 2) return -1;
174 repeated = 2;
175 /* to state3 */
176
177 state3:
178 /* We have read 'repeated' occurrences of 'last', 'in' is positioned on
179 the last octet read. It is true that 2 <= repeated < 128 and
180 2 <= available. */
181 do {
182 if (in == end) break;
183 in++;
184 if (*in != last) break;
185 repeated++;
186 } while (repeated < 128);
187
188 /* Had to stop accumulating, for whatever reason. Write results. */
189 *out = neg(-repeated + 1); out++; /* control byte */
190 *out = last; out++;
191 available -= 2;
192
193 /* Decide where to go from here */
194 if (*in != last) goto state1;
195 if (in == end) goto finished;
196 in++;
197 goto state1;
198
199 state4:
200 /* We have read 'stored'+2 octets, 0 <= stored <= 126. All except the
201 last two have already been stored before the current value of 'out',
202 leaving space for the control byte at out[-stored-1]. The last two
203 octets can be found in 'last' and '*in', and they are not identical.
204 We also know that 'available' >= 2.
205 */
206 do {
207 *out = last; stored++; available--; out++;
208 if (in == end) {
209 *out = *in; stored++; available--;
210 out[-stored] = stored - 1; /* control byte */
211 goto finished;
212 }
213 if (available < 2) return -1;
214 last = *in;
215 in++;
216 } while (*in != last && stored <= 126);
217
218 if (*in == last) {
219 if (stored < 126) goto state5;
220 out[-stored-1] = stored - 1; /* control byte */
221 repeated = 2;
222 goto state3;
223 }
224
225 /* stored == 127, available >= 2 */
226 *out = last; stored++; available--; out++;
227 out[-stored-1] = stored - 1; /* control byte */
228 goto state1;
229
230 state5:
231 /* We have read 'stored'+2 octets, 'stored' < 126. 'stored' of them have
232 been stored before 'out' with the control byte still to be written to
233 out[-stored-1]. The last two octets can be found in 'last' and '*in',
234 and they are identical. We also know 2 <= available. */
235 if (in == end) {
236 *out = last; out++;
237 *out = *in;
238 stored += 2; available -= 2;
239 out[-stored] = stored - 1; /* control byte */
240 goto finished;
241 }
242 in++;
243 if (*in == last) {
244 out[-stored-1] = stored - 1; /* control byte */
245 repeated = 3;
246 goto state3;
247 }
248 if (available < 3) return -1;
249 *out = last; stored++; available--; out++; /* The first repeated octet */
250 goto state4;
251
252 finished:
253 return maxoutcount - available;
254 }
255
256 #undef neg
257
258 /******************************************************************************
259
260 Function: write_delta_replacement
261
262 This function writes a replacement string for delta compression (method 3),
263 i.e. the sequence of command byte, optional extension offset bytes, and the
264 replacement bytes.
265
266 'out' points to a sequence of at least 'available' octets to which the string
267 is to be written. 'reloffset' is the "left offset" value for the replacement.
268 'in' points to a sequence of 'replace_count' octets to be replaced.
269 'replace_count' must lie between 1 and 8, inclusive.
270
271 This function returns a negative value on error or the number of octets
272 written otherwise.
273
274 ******************************************************************************/
275
write_delta_replacement(pcl_Octet * out,int available,int reloffset,const pcl_Octet * in,int replace_count)276 static int write_delta_replacement(pcl_Octet *out, int available, int reloffset,
277 const pcl_Octet *in, int replace_count)
278 {
279 int used;
280 assert(1 <= replace_count && replace_count <= 8);
281
282 /* Prepare the command byte and, possibly, the extension offset bytes */
283 used = 1;
284 if (available < used) return -1;
285 *out = (replace_count - 1) << 5;
286 if (reloffset < 31) {
287 *out++ += reloffset;
288 }
289 else {
290 /* Large offset */
291 *out++ += 31;
292 reloffset -= 31;
293 used += reloffset/255 + 1;
294 if (available < used) return -1;
295 while (reloffset >= 255) {
296 *out++ = 255;
297 reloffset -= 255;
298 }
299 *out++ = reloffset;
300 }
301
302 /* Transfer the replacement bytes */
303 used += replace_count;
304 if (available < used) return -1;
305 while (replace_count > 0) {
306 *out++ = *in++;
307 replace_count--;
308 }
309
310 return used;
311 }
312
313 /******************************************************************************
314
315 Function: compress_delta
316
317 This function performs delta row compression (method 3).
318
319 The pairs (in, incount) and (prev, prevcount) describe the row to be
320 compressed and the row sent last (seed row), of course in uncompressed
321 form. (out, maxcount) refers to a storage area of 'maxoutcount' length to
322 which the compressed result for 'in' is to be written.
323 All three octet strings must be valid and any may be zero.
324
325 It is assumed that any difference in length between 'in' and 'prev' is
326 (logically) due to trailing zero octets having been suppressed in the shorter
327 of the two.
328
329 The function returns the number of octets written to 'out' (a zero value is
330 possible and refers to a row identical with the one sent last), or a negative
331 value on error.
332
333 ******************************************************************************/
334
335 /* First a macro needed several times for comparing old and new row.
336 Because we really need string substitution for the 'invalue', 'prevvalue'
337 and 'repstart' parameters this cannot be realized by a function.
338 This loop depends on the following variables external to it:
339 pos, absoffset, out, opos, maxoutcount.
340 */
341 #define delta_loop(bound, invalue, prevvalue, repstart) \
342 while (pos < bound) { \
343 if (invalue != prevvalue) { \
344 int reloffset = pos - absoffset; /* "left offset" */ \
345 absoffset = pos; /* first different octet */ \
346 \
347 /* Collect different octets, at most 8 */ \
348 do pos++; \
349 while (pos < bound && pos < absoffset + 8 && invalue != prevvalue); \
350 /* All the octets with positions in [absoffset, pos) have to */ \
351 /* be replaced, and there are at most 8 of them. */ \
352 \
353 /* Write the replacement string */ \
354 { \
355 int written; \
356 written = write_delta_replacement(out + opos, maxoutcount - opos, \
357 reloffset, repstart, pos - absoffset); \
358 if (written < 0) return -1; \
359 opos += written; \
360 } \
361 absoffset = pos; \
362 } \
363 else pos++; \
364 }
365
compress_delta(const pcl_Octet * in,int incount,const pcl_Octet * prev,int prevcount,pcl_Octet * out,int maxoutcount)366 static int compress_delta(const pcl_Octet *in, int incount,
367 const pcl_Octet *prev, int prevcount, pcl_Octet *out, int maxoutcount)
368 {
369 int
370 absoffset, /* absolute offset (starting with zero) */
371 mincount, /* min(incount, prevcount) */
372 opos, /* next position in the output */
373 pos; /* next position in the input rows */
374
375 /* Treat the special case of a zero output buffer (actually, the bad case is
376 merely the one where 'out' is NULL) */
377 if (maxoutcount == 0) {
378 if (incount == prevcount &&
379 (incount == 0 || memcmp(in, prev, incount) == 0)) return 0;
380 /* Can there be machines where memcmp() compares bits beyond those
381 used for the 'pcl_Octet's? Unlikely. */
382 return -1;
383 }
384
385 /* Initialization */
386 mincount = (incount < prevcount? incount: prevcount);
387 pos = 0; opos = 0;
388 absoffset = 0; /* first untreated octet, i.e. position after the last
389 unaltered octet. */
390
391 /* Loop over parts common to this and the last row */
392 delta_loop(mincount, in[pos], prev[pos], in + absoffset);
393 /* Note: This artificial separation at the 'mincount' position (logically,
394 both rows have equal length) is simpler to program but can result in
395 the compressed row being 1 octet longer than necessary. */
396
397 /* Treat length differences between 'in' and 'prev'. */
398 if (mincount < incount) {
399 /* We have to send all octets in the 'in' row which are non-zero. */
400 delta_loop(incount, in[pos], 0, in + absoffset);
401 }
402 else {
403 /* We have to replace all non-zero octets in the previous row. */
404 pcl_Octet zero_block[8] = {0, 0, 0, 0, 0, 0, 0, 0};
405 delta_loop(prevcount, 0, prev[pos], zero_block);
406 }
407 assert(opos <= maxoutcount);
408
409 return opos;
410 }
411
412 #undef delta_loop
413
414 /******************************************************************************
415
416 Function: write_crdr_header
417
418 This function writes the header for compressed replacement delta row encoding
419 (method 9). It returns the number of octets written or a negative value on
420 error.
421
422 ******************************************************************************/
423
write_crdr_header(pcl_bool compressed,pcl_Octet * out,int maxoutcount,int reloffset,int repcount)424 static int write_crdr_header(pcl_bool compressed, pcl_Octet *out,
425 int maxoutcount, int reloffset, int repcount)
426 {
427 int
428 maxvalue,
429 shift,
430 used = 1; /* command byte */
431
432 /* The command byte */
433 if (maxoutcount < 1) return -1;
434 if (compressed) *out = 0x80; /* control bit: compressed */
435 else *out = 0; /* control bit: uncompressed */
436 maxvalue = (compressed? 3: 15);
437 shift = (compressed? 5: 3);
438 if (reloffset < maxvalue) {
439 *out += reloffset << shift;
440 reloffset = -1;
441 }
442 else {
443 *out += maxvalue << shift;
444 reloffset -= maxvalue;
445 }
446 /* The value to be encoded for 'repcount' is different from 'repcount': */
447 if (compressed) repcount -= 2;
448 else repcount--;
449 assert(repcount >= 0);
450 maxvalue = (compressed? 31: 7);
451 if (repcount < maxvalue) {
452 *out += repcount;
453 repcount = -1;
454 }
455 else {
456 *out += maxvalue;
457 repcount -= maxvalue;
458 }
459 out++;
460
461 /* Optional offset bytes */
462 while (reloffset >= 0) {
463 if (used >= maxoutcount) return -1;
464 *out++ = (reloffset >= 255? 255: reloffset);
465 reloffset -= 255;
466 used++;
467 }
468
469 /* Optional replacement count bytes */
470 while (repcount >= 0) {
471 if (used >= maxoutcount) return -1;
472 *out++ = (repcount >= 255? 255: repcount);
473 repcount -= 255;
474 used++;
475 }
476
477 return used;
478 }
479
480 /******************************************************************************
481
482 Function: write_crdr_uncompressed
483
484 This function returns the number of octets written or a negative value on
485 error.
486
487 'in' may be NULL, indicating a sequence of 'repcount' null octets.
488 This case is practically irrelevant except for 'repcount' == 1.
489
490 ******************************************************************************/
491
write_crdr_uncompressed(pcl_Octet * out,int maxoutcount,int reloffset,const pcl_Octet * in,int repcount)492 static int write_crdr_uncompressed(pcl_Octet *out, int maxoutcount,
493 int reloffset, const pcl_Octet *in, int repcount)
494 {
495 int used = write_crdr_header(FALSE, out, maxoutcount, reloffset, repcount);
496 if (used < 0 || used + repcount > maxoutcount) return -1;
497
498 out += used;
499 if (in == NULL) memset(out, 0, repcount*sizeof(pcl_Octet));
500 else memcpy(out, in, repcount*sizeof(pcl_Octet));
501
502 return used + repcount;
503 }
504
505 /******************************************************************************
506
507 Function: write_crdr_compressed
508
509 This function returns the number of octets written or a negative value on
510 error.
511
512 ******************************************************************************/
513
write_crdr_compressed(pcl_Octet * out,int maxoutcount,int reloffset,pcl_Octet in,int repeat_count)514 static int write_crdr_compressed(pcl_Octet *out, int maxoutcount, int reloffset,
515 pcl_Octet in, int repeat_count)
516 {
517 int used = write_crdr_header(TRUE, out, maxoutcount, reloffset, repeat_count);
518 if (used < 0 || used >= maxoutcount) return -1;
519
520 out += used;
521 *out = in;
522
523 return used + 1;
524 }
525
526 /******************************************************************************
527
528 Function: write_crdr_replacement
529
530 This function returns the number of octets written to 'out' or a negative
531 value on error.
532
533 'in' may be NULL, indicating a sequence of 'repcount' null octets.
534 'repcount' must be positive.
535
536 ******************************************************************************/
537
write_crdr_replacement(pcl_Octet * out,int maxoutcount,int reloffset,const pcl_Octet * in,int repcount)538 static int write_crdr_replacement(pcl_Octet *out, int maxoutcount,
539 int reloffset, const pcl_Octet *in, int repcount)
540 {
541 const pcl_Octet *final;
542 int written = 0;
543
544 /* Treat the case of a null sequence */
545 if (in == NULL) {
546 if (repcount == 1)
547 return write_crdr_uncompressed(out, maxoutcount, reloffset, in, repcount);
548 return write_crdr_compressed(out, maxoutcount, reloffset, 0, repcount);
549 }
550
551 /* Loop over 'in', dividing it into sections at the boundaries of
552 sequences of repeated octets. */
553 final = in + repcount - 1;
554 while (repcount > 0) {
555 /* Advance 'bdup' over non-repeated octet */
556 const pcl_Octet *bdup;
557 bdup = in;
558 while (bdup < final && *bdup != *(bdup + 1)) bdup++;
559
560 /* If there is something either before a repeated section or before the
561 end, encode it uncompressed. */
562 if (in < bdup || bdup == final) {
563 int incount = (bdup == final? repcount: bdup - in);
564 int rc;
565 rc = write_crdr_uncompressed(out + written, maxoutcount - written,
566 reloffset, in, incount);
567 if (rc < 0) return rc;
568 written += rc;
569 reloffset = 0;
570 repcount -= incount;
571 if (repcount > 0) in += incount;
572 }
573
574 /* If we have encountered a repeated section, encode it compressed.
575 Note that the compressed version for a repetition is never longer than
576 the uncompressed one, not even for a repeat count of 2, although in this
577 case it might have equal length depending on the offset. */
578 if (bdup < final) {
579 const pcl_Octet *edup = bdup + 1;
580 int incount, rc;
581 while (edup < final && *(edup + 1) == *bdup) edup++;
582 incount = edup - bdup + 1;
583 rc = write_crdr_compressed(out + written, maxoutcount - written,
584 reloffset, *bdup, incount);
585 if (rc < 0) return rc;
586 written += rc;
587 reloffset = 0;
588 repcount -= incount;
589 if (repcount > 0) in = edup + 1;
590 }
591 }
592
593 return written;
594 }
595
596 /******************************************************************************
597
598 Function: compress_crdr
599
600 This function performs compressed replacement delta row encoding (compression
601 method 9).
602
603 The pairs (in, incount) and (prev, prevcount) describe the row to be
604 compressed and the row sent last (seed row), of course in uncompressed
605 form. (out, maxcount) refers to a storage area of 'maxoutcount' length to
606 which the compressed result for 'in' is to be written.
607 All three octet strings must be valid and any may be zero.
608
609 It is assumed that any difference in length between 'in' and 'prev' is
610 (logically) due to trailing zero octets having been suppressed in the shorter
611 of the two.
612
613 The function returns the number of octets written to 'out' (a zero value is
614 possible and refers to a row identical with the one sent last), or a negative
615 value on error.
616
617 This function and those it calls are very similar to the functions for delta
618 row compression.
619
620 ******************************************************************************/
621
622 /* Again, as for delta row compression, I'm using a macro for comparison. */
623 #define crdr_loop(bound, invalue, prevvalue, repstart) \
624 while (pos < bound) { \
625 if (invalue == prevvalue) pos++; \
626 else { \
627 int reloffset = pos - absoffset, written; \
628 absoffset = pos; \
629 do pos++; while (pos < bound && invalue != prevvalue); \
630 \
631 written = write_crdr_replacement(out + opos, maxoutcount - opos, \
632 reloffset, repstart, pos - absoffset); \
633 if (written < 0) return written; \
634 absoffset = pos; \
635 opos += written; \
636 } \
637 }
638
compress_crdr(const pcl_Octet * in,int incount,const pcl_Octet * prev,int prevcount,pcl_Octet * out,int maxoutcount)639 static int compress_crdr(const pcl_Octet *in, int incount,
640 const pcl_Octet *prev, int prevcount, pcl_Octet *out, int maxoutcount)
641 {
642 int
643 absoffset = 0,
644 mincount = (incount < prevcount? incount: prevcount),
645 opos = 0,
646 pos = 0;
647
648 /* Treat the special case of a zero output buffer (again, the bad case is
649 merely the one where 'out' is NULL) */
650 if (maxoutcount == 0) {
651 if (incount == prevcount &&
652 (incount == 0 || memcmp(in, prev, incount) == 0)) return 0;
653 return -1;
654 }
655
656 crdr_loop(mincount, in[pos], prev[pos], in + absoffset);
657 if (mincount < incount) {
658 crdr_loop(incount, in[pos], 0, in + absoffset);
659 }
660 else {
661 crdr_loop(prevcount, 0, prev[pos], NULL);
662 }
663
664 return opos;
665 }
666
667 #undef crdr_loop
668
669 /******************************************************************************
670
671 Function: pcl_compress
672
673 This function compresses an octet string using the compression algorithm
674 specified by 'method'.
675
676 The arguments 'in' and 'out' must be non-NULL. They point to the data to be
677 compressed ('in->length' octets starting at 'in->str') and the area to which
678 the compressed result should be written (at most 'out->length' octets
679 starting at 'out->str'). If '*in' and '*out' are both non-zero (see
680 definitions above), their storage areas ('in->str' to
681 'in->str + in->length - 1' and 'out->str' to 'out->str + out->length - 1')
682 should not overlap.
683
684 If 'method' refers to a method entailing "vertical" compression, i.e.
685 compression with respect to an octet string previously sent, 'prev' must
686 point to this previous string. This is the case for methods 3 and 9.
687 The variable is ignored otherwise and may be NULL. If it is needed, it should
688 not overlap with the storage area of '*out'.
689
690 The function returns a non-zero value in case of insufficient space in '*out'.
691 In that situation, part of 'out->str' may have been overwritten already.
692 Otherwise, a value of zero is returned and 'out->length' is set to the number
693 of octets the compressed result occupies in 'out->str'.
694
695 ******************************************************************************/
696
697 /* Test macro for an argument of type "pcl_OctetString *" */
698 #define is_valid(s) \
699 (s != NULL && ((s)->length == 0 || (s)->length > 0 && (s)->str != NULL))
700
pcl_compress(pcl_Compression method,const pcl_OctetString * in,const pcl_OctetString * prev,pcl_OctetString * out)701 int pcl_compress(pcl_Compression method, const pcl_OctetString *in,
702 const pcl_OctetString *prev, pcl_OctetString *out)
703 {
704 int result = -1;
705
706 /* Prevent silly mistakes with the arguments */
707 assert(is_valid(in) && is_valid(out) &&
708 (method != pcl_cm_delta && method != pcl_cm_crdr || is_valid(prev)));
709
710 /* Treat zero-length case for the "purely horizontal" methods */
711 if (in->length == 0 && method != pcl_cm_delta && method != pcl_cm_crdr) {
712 out->length = 0;
713 return 0;
714 }
715
716 switch (method) {
717 case pcl_cm_none: /* oh, well... */
718 if (out->length <= in->length) {
719 memcpy(out->str, in->str, in->length*sizeof(pcl_Octet));
720 result = in->length;
721 }
722 break;
723 case pcl_cm_rl:
724 result = compress_runlength(in->str, in->length, out->str, out->length);
725 break;
726 case pcl_cm_tiff:
727 result = compress_tiff(in->str, in->length, out->str, out->length);
728 break;
729 case pcl_cm_delta:
730 result = compress_delta(in->str, in->length, prev->str, prev->length,
731 out->str, out->length);
732 break;
733 case pcl_cm_crdr:
734 result = compress_crdr(in->str, in->length, prev->str, prev->length,
735 out->str, out->length);
736 break;
737 default:
738 assert(0); /* Illegal value for compression method */
739 /* If NDEBUG is defined, we fall through with the default value for
740 'result' which is -1, i.e., failure. */
741 }
742
743 /* Assign the length of the output octet string */
744 if (result >= 0) {
745 out->length = result;
746 result = 0;
747 }
748
749 return result;
750 }
751