/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 | } |