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