Coverage Report

Created: 2025-08-28 07:06

/src/ghostpdl/contrib/pcl3/src/pclcomp.c
Line
Count
Source (jump to first uncovered line)
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
0
#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
75
static int compress_runlength(const pcl_Octet *in, int incount, pcl_Octet *out,
76
  int maxoutcount)
77
0
{
78
0
  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
0
  while (incount > 0 && available > 1) {
84
0
    int count = 0;
85
86
0
    out++;  /* skip repetition count octet, to be filled in later */
87
0
    *out = *in;
88
0
    do {
89
0
      in++; incount--; count++;
90
0
    } while (incount > 0 && *in == *out && count < 256);
91
0
    *(out - 1) = count - 1;
92
0
    out++; available -= 2;
93
0
  }
94
95
0
  if (incount > 0) return -1;
96
0
  return maxoutcount - available;
97
0
}
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
134
static int compress_tiff(const pcl_Octet *in, int incount, pcl_Octet *out,
135
  int maxoutcount)
136
0
{
137
0
  pcl_Octet
138
0
    last; /* a remembered octet before the current 'in' value */
139
0
  const pcl_Octet
140
0
    *end = in + incount - 1;  /* last position in 'in' */
141
0
  int
142
0
    available = maxoutcount,  /* number of free octets left in 'out' */
143
0
    repeated, /* repeat count during a compressed sequence */
144
0
    stored; /* store count during a non-compressed sequence */
145
146
0
  state1:
147
    /* No octet is held over to be treated, 'in' points to the next one */
148
0
    if (in == end) {
149
      /* This is the last octet and a single one. */
150
0
      if (available < 2) return -1;
151
0
      *out = 0; out++;  /* control byte */
152
0
      *out = *in;
153
0
      available -= 2;
154
0
      goto finished;
155
0
    }
156
0
    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
0
    if (*in != last) {
162
0
      if (available < 3) return -1;
163
0
      out++; available--; /* Skip control byte to be filled in later */
164
0
      stored = 0;
165
0
      goto state4;
166
0
    }
167
0
    if (available < 2) return -1;
168
0
    repeated = 2;
169
    /* to state3 */
170
171
0
  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
0
    do {
176
0
      if (in == end) break;
177
0
      in++;
178
0
      if (*in != last) break;
179
0
      repeated++;
180
0
    } while (repeated < 128);
181
182
    /* Had to stop accumulating, for whatever reason. Write results. */
183
0
    *out = neg(-repeated + 1); out++; /* control byte */
184
0
    *out = last; out++;
185
0
    available -= 2;
186
187
    /* Decide where to go from here */
188
0
    if (*in != last) goto state1;
189
0
    if (in == end) goto finished;
190
0
    in++;
191
0
    goto state1;
192
193
0
  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
0
    do {
201
0
      *out = last; stored++; available--; out++;
202
0
      if (in == end) {
203
0
        *out = *in; stored++; available--;
204
0
        out[-stored] = stored - 1; /* control byte */
205
0
        goto finished;
206
0
      }
207
0
      if (available < 2) return -1;
208
0
      last = *in;
209
0
      in++;
210
0
    } while (*in != last && stored <= 126);
211
212
0
    if (*in == last) {
213
0
      if (stored < 126) goto state5;
214
0
      out[-stored-1] = stored - 1; /* control byte */
215
0
      repeated = 2;
216
0
      goto state3;
217
0
    }
218
219
    /* stored == 127, available >= 2 */
220
0
    *out = last; stored++; available--; out++;
221
0
    out[-stored-1] = stored - 1; /* control byte */
222
0
    goto state1;
223
224
0
  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
0
    if (in == end) {
230
0
      *out = last; out++;
231
0
      *out = *in;
232
0
      stored += 2; available -= 2;
233
0
      out[-stored] = stored - 1; /* control byte */
234
0
      goto finished;
235
0
    }
236
0
    in++;
237
0
    if (*in == last) {
238
0
      out[-stored-1] = stored - 1;  /* control byte */
239
0
      repeated = 3;
240
0
      goto state3;
241
0
    }
242
0
    if (available < 3) return -1;
243
0
    *out = last; stored++; available--; out++; /* The first repeated octet */
244
0
    goto state4;
245
246
0
  finished:
247
0
  return maxoutcount - available;
248
0
}
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
270
static int write_delta_replacement(pcl_Octet *out, int available, int reloffset,
271
  const pcl_Octet *in, int replace_count)
272
0
{
273
0
  int used;
274
0
  assert(1 <= replace_count && replace_count <= 8);
275
276
  /* Prepare the command byte and, possibly, the extension offset bytes */
277
0
  used = 1;
278
0
  if (available < used) return -1;
279
0
  *out = (replace_count - 1) << 5;
280
0
  if (reloffset < 31) {
281
0
    *out++ += reloffset;
282
0
  }
283
0
  else {
284
    /* Large offset */
285
0
    *out++ += 31;
286
0
    reloffset -= 31;
287
0
    used += reloffset/255 + 1;
288
0
    if (available < used) return -1;
289
0
    while (reloffset >= 255) {
290
0
      *out++ = 255;
291
0
      reloffset -= 255;
292
0
    }
293
0
    *out++ = reloffset;
294
0
  }
295
296
  /* Transfer the replacement bytes */
297
0
  used += replace_count;
298
0
  if (available < used) return -1;
299
0
  while (replace_count > 0) {
300
0
    *out++ = *in++;
301
0
    replace_count--;
302
0
  }
303
304
0
  return used;
305
0
}
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
0
  while (pos < bound) {             \
337
0
    if (invalue != prevvalue) {           \
338
0
      int reloffset = pos - absoffset;  /* "left offset" */   \
339
0
      absoffset = pos;  /* first different octet */     \
340
0
                                                                        \
341
0
      /* Collect different octets, at most 8 */       \
342
0
      do pos++;               \
343
0
      while (pos < bound && pos < absoffset + 8 && invalue != prevvalue); \
344
0
      /* All the octets with positions in [absoffset, pos) have to */ \
345
0
      /* be replaced, and there are at most 8 of them. */   \
346
0
                                                                        \
347
0
      /* Write the replacement string */        \
348
0
      {                 \
349
0
        int written;              \
350
0
        written = write_delta_replacement(out + opos, maxoutcount - opos, \
351
0
          reloffset, repstart, pos - absoffset);      \
352
0
        if (written < 0) return -1;         \
353
0
        opos += written;            \
354
0
      }                 \
355
0
      absoffset = pos;              \
356
0
    }                  \
357
0
    else pos++;               \
358
0
  }
359
360
static int compress_delta(const pcl_Octet *in, int incount,
361
  const pcl_Octet *prev, int prevcount, pcl_Octet *out, int maxoutcount)
362
0
{
363
0
  int
364
0
    absoffset,  /* absolute offset (starting with zero) */
365
0
    mincount, /* min(incount, prevcount) */
366
0
    opos, /* next position in the output */
367
0
    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
0
  if (maxoutcount == 0) {
372
0
    if (incount == prevcount &&
373
0
      (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
0
    return -1;
377
0
  }
378
379
  /* Initialization */
380
0
  mincount = (incount < prevcount? incount: prevcount);
381
0
  pos = 0; opos = 0;
382
0
  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
0
  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
0
  if (mincount < incount) {
393
    /* We have to send all octets in the 'in' row which are non-zero. */
394
0
    delta_loop(incount, in[pos], 0, in + absoffset);
395
0
  }
396
0
  else {
397
    /* We have to replace all non-zero octets in the previous row. */
398
0
    pcl_Octet zero_block[8] = {0, 0, 0, 0, 0, 0, 0, 0};
399
0
    delta_loop(prevcount, 0, prev[pos], zero_block);
400
0
  }
401
0
  assert(opos <= maxoutcount);
402
403
0
  return opos;
404
0
}
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
418
static int write_crdr_header(pcl_bool compressed, pcl_Octet *out,
419
  int maxoutcount, int reloffset, int repcount)
420
0
{
421
0
  int
422
0
    maxvalue,
423
0
    shift,
424
0
    used = 1; /* command byte */
425
426
  /* The command byte */
427
0
  if (maxoutcount < 1) return -1;
428
0
  if (compressed) *out = 0x80; /* control bit: compressed */
429
0
  else *out = 0;   /* control bit: uncompressed */
430
0
  maxvalue = (compressed? 3: 15);
431
0
  shift = (compressed? 5: 3);
432
0
  if (reloffset < maxvalue) {
433
0
    *out += reloffset << shift;
434
0
    reloffset = -1;
435
0
  }
436
0
  else {
437
0
    *out += maxvalue << shift;
438
0
    reloffset -= maxvalue;
439
0
  }
440
  /* The value to be encoded for 'repcount' is different from 'repcount': */
441
0
  if (compressed) repcount -= 2;
442
0
  else repcount--;
443
0
  assert(repcount >= 0);
444
0
  maxvalue = (compressed? 31: 7);
445
0
  if (repcount < maxvalue) {
446
0
    *out += repcount;
447
0
    repcount = -1;
448
0
  }
449
0
  else {
450
0
    *out += maxvalue;
451
0
    repcount -= maxvalue;
452
0
  }
453
0
  out++;
454
455
  /* Optional offset bytes */
456
0
  while (reloffset >= 0) {
457
0
    if (used >= maxoutcount) return -1;
458
0
    *out++ = (reloffset >= 255? 255: reloffset);
459
0
    reloffset -= 255;
460
0
    used++;
461
0
  }
462
463
  /* Optional replacement count bytes */
464
0
  while (repcount >= 0) {
465
0
    if (used >= maxoutcount) return -1;
466
0
    *out++ = (repcount >= 255? 255: repcount);
467
0
    repcount -= 255;
468
0
    used++;
469
0
  }
470
471
0
  return used;
472
0
}
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
486
static int write_crdr_uncompressed(pcl_Octet *out, int maxoutcount,
487
  int reloffset, const pcl_Octet *in, int repcount)
488
0
{
489
0
  int used = write_crdr_header(FALSE, out, maxoutcount, reloffset, repcount);
490
0
  if (used < 0 || used + repcount > maxoutcount) return -1;
491
492
0
  out += used;
493
0
  if (in == NULL) memset(out, 0, repcount*sizeof(pcl_Octet));
494
0
  else memcpy(out, in, repcount*sizeof(pcl_Octet));
495
496
0
  return used + repcount;
497
0
}
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
508
static int write_crdr_compressed(pcl_Octet *out, int maxoutcount, int reloffset,
509
  pcl_Octet in, int repeat_count)
510
0
{
511
0
  int used = write_crdr_header(TRUE, out, maxoutcount, reloffset, repeat_count);
512
0
  if (used < 0 || used >= maxoutcount) return -1;
513
514
0
  out += used;
515
0
  *out = in;
516
517
0
  return used + 1;
518
0
}
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
532
static int write_crdr_replacement(pcl_Octet *out, int maxoutcount,
533
  int reloffset, const pcl_Octet *in, int repcount)
534
0
{
535
0
  const pcl_Octet *final;
536
0
  int written = 0;
537
538
  /* Treat the case of a null sequence */
539
0
  if (in == NULL) {
540
0
    if (repcount == 1)
541
0
      return write_crdr_uncompressed(out, maxoutcount, reloffset, in, repcount);
542
0
    return write_crdr_compressed(out, maxoutcount, reloffset, 0, repcount);
543
0
  }
544
545
  /* Loop over 'in', dividing it into sections at the boundaries of
546
     sequences of repeated octets. */
547
0
  final = in + repcount - 1;
548
0
  while (repcount > 0) {
549
    /* Advance 'bdup' over non-repeated octet */
550
0
    const pcl_Octet *bdup;
551
0
    bdup = in;
552
0
    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
0
    if (in < bdup || bdup == final) {
557
0
      int incount = (bdup == final? repcount: bdup - in);
558
0
      int rc;
559
0
      rc = write_crdr_uncompressed(out + written, maxoutcount - written,
560
0
        reloffset, in, incount);
561
0
      if (rc < 0) return rc;
562
0
      written += rc;
563
0
      reloffset = 0;
564
0
      repcount -= incount;
565
0
      if (repcount > 0) in += incount;
566
0
    }
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
0
    if (bdup < final) {
573
0
      const pcl_Octet *edup = bdup + 1;
574
0
      int incount, rc;
575
0
      while (edup < final && *(edup + 1) == *bdup) edup++;
576
0
      incount = edup - bdup + 1;
577
0
      rc = write_crdr_compressed(out + written, maxoutcount - written,
578
0
        reloffset, *bdup, incount);
579
0
      if (rc < 0) return rc;
580
0
      written += rc;
581
0
      reloffset = 0;
582
0
      repcount -= incount;
583
0
      if (repcount > 0) in = edup + 1;
584
0
    }
585
0
  }
586
587
0
  return written;
588
0
}
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
0
  while (pos < bound) {             \
619
0
    if (invalue == prevvalue) pos++;         \
620
0
    else {               \
621
0
      int reloffset = pos - absoffset, written;       \
622
0
      absoffset = pos;              \
623
0
      do pos++; while (pos < bound && invalue != prevvalue);    \
624
0
                                                                        \
625
0
      written = write_crdr_replacement(out + opos, maxoutcount - opos,  \
626
0
        reloffset, repstart, pos - absoffset);        \
627
0
      if (written < 0) return written;         \
628
0
      absoffset = pos;              \
629
0
      opos += written;              \
630
0
    }                  \
631
0
  }
632
633
static int compress_crdr(const pcl_Octet *in, int incount,
634
  const pcl_Octet *prev, int prevcount, pcl_Octet *out, int maxoutcount)
635
0
{
636
0
  int
637
0
    absoffset = 0,
638
0
    mincount = (incount < prevcount? incount: prevcount),
639
0
    opos = 0,
640
0
    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
0
  if (maxoutcount == 0) {
645
0
    if (incount == prevcount &&
646
0
      (incount == 0 || memcmp(in, prev, incount) == 0)) return 0;
647
0
    return -1;
648
0
  }
649
650
0
  crdr_loop(mincount, in[pos], prev[pos], in + absoffset);
651
0
  if (mincount < incount) {
652
0
    crdr_loop(incount, in[pos], 0, in + absoffset);
653
0
  }
654
0
  else {
655
0
    crdr_loop(prevcount, 0, prev[pos], NULL);
656
0
  }
657
658
0
  return opos;
659
0
}
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
695
int pcl_compress(pcl_Compression method, const pcl_OctetString *in,
696
  const pcl_OctetString *prev, pcl_OctetString *out)
697
0
{
698
0
  int result = -1;
699
700
  /* Prevent silly mistakes with the arguments */
701
0
  assert((is_valid(in) && is_valid(out) &&
702
0
          method != pcl_cm_delta && method != pcl_cm_crdr) || is_valid(prev));
703
704
  /* Treat zero-length case for the "purely horizontal" methods */
705
0
  if (in->length == 0 && method != pcl_cm_delta && method != pcl_cm_crdr) {
706
0
    out->length = 0;
707
0
    return 0;
708
0
  }
709
710
0
  switch (method) {
711
0
  case pcl_cm_none: /* oh, well... */
712
0
    if (out->length <= in->length) {
713
0
      memcpy(out->str, in->str, in->length*sizeof(pcl_Octet));
714
0
      result = in->length;
715
0
    }
716
0
    break;
717
0
  case pcl_cm_rl:
718
0
    result = compress_runlength(in->str, in->length, out->str, out->length);
719
0
    break;
720
0
  case pcl_cm_tiff:
721
0
    result = compress_tiff(in->str, in->length, out->str, out->length);
722
0
    break;
723
0
  case pcl_cm_delta:
724
0
    result = compress_delta(in->str, in->length, prev->str, prev->length,
725
0
      out->str, out->length);
726
0
    break;
727
0
  case pcl_cm_crdr:
728
0
    result = compress_crdr(in->str, in->length, prev->str, prev->length,
729
0
      out->str, out->length);
730
0
    break;
731
0
  default:
732
0
    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
0
  }
736
737
  /* Assign the length of the output octet string */
738
0
  if (result >= 0) {
739
0
    out->length = result;
740
0
    result = 0;
741
0
  }
742
743
0
  return result;
744
0
}