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