1 /* Copyright (C) 1994, 1995, 1998, 1999 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: sbwbs.c,v 1.5 2002/06/16 03:58:14 lpd Exp $ */
18 /* Burrows/Wheeler block sorting compression filters */
19 #include "stdio_.h"
20 #include "memory_.h"
21 #include <stdlib.h> /* for qsort */
22 #include "gdebug.h"
23 #include "strimpl.h"
24 #include "sfilter.h"
25 #include "sbwbs.h"
26
27 /* ------ Common code for streams that buffer a block ------ */
28
29 private_st_buffered_state();
30
31 /* Initialize */
32 private void
s_buffered_set_defaults(stream_state * st)33 s_buffered_set_defaults(stream_state * st)
34 {
35 stream_buffered_state *const ss = (stream_buffered_state *) st;
36
37 /* Clear pointers */
38 ss->buffer = 0;
39 }
40 private int
s_buffered_no_block_init(stream_state * st)41 s_buffered_no_block_init(stream_state * st)
42 {
43 stream_buffered_state *const ss = (stream_buffered_state *) st;
44
45 ss->buffer = 0;
46 ss->filling = true;
47 ss->bpos = 0;
48 return 0;
49 }
50 private int
s_buffered_block_init(stream_state * st)51 s_buffered_block_init(stream_state * st)
52 {
53 stream_buffered_state *const ss = (stream_buffered_state *) st;
54
55 s_buffered_no_block_init(st);
56 ss->buffer = gs_alloc_bytes(st->memory, ss->BlockSize, "buffer");
57 if (ss->buffer == 0)
58 return ERRC;
59 /****** WRONG ******/
60 return 0;
61 }
62
63 /* Continue filling the buffer if needed. */
64 /* Return 0 if the buffer isn't full yet, 1 if it is full or if */
65 /* we reached the end of input data. */
66 /* In the latter case, also set filling = false. */
67 /* Note that this procedure doesn't take pw as an argument. */
68 private int
s_buffered_process(stream_state * st,stream_cursor_read * pr,bool last)69 s_buffered_process(stream_state * st, stream_cursor_read * pr, bool last)
70 {
71 stream_buffered_state *const ss = (stream_buffered_state *) st;
72 register const byte *p = pr->ptr;
73 const byte *rlimit = pr->limit;
74 uint count = rlimit - p;
75 uint left = ss->bsize - ss->bpos;
76
77 if (!ss->filling)
78 return 1;
79 if (left < count)
80 count = left;
81 if_debug3('w', "[w]buffering %d bytes to position %d, last = %s\n",
82 count, ss->bpos, (last ? "true" : "false"));
83 memcpy(ss->buffer + ss->bpos, p + 1, count);
84 pr->ptr = p += count;
85 ss->bpos += count;
86 if (ss->bpos == ss->bsize || (p == rlimit && last)) {
87 ss->filling = false;
88 return 1;
89 }
90 return 0;
91 }
92
93 /* Release */
94 private void
s_buffered_release(stream_state * st)95 s_buffered_release(stream_state * st)
96 {
97 stream_buffered_state *const ss = (stream_buffered_state *) st;
98
99 gs_free_object(st->memory, ss->buffer, "buffer");
100 }
101
102 /* ------ Common code for Burrows/Wheeler block sorting filters ------ */
103
104 private_st_BWBS_state();
105 private void s_BWBS_release(stream_state *);
106
107 /* Set default parameter values (actually, just clear pointers). */
108 private void
s_BWBS_set_defaults(stream_state * st)109 s_BWBS_set_defaults(stream_state * st)
110 {
111 stream_BWBS_state *const ss = (stream_BWBS_state *) st;
112
113 s_buffered_set_defaults(st);
114 ss->offsets = 0;
115 }
116
117 /* Initialize */
118 private int
bwbs_init(stream_state * st,uint osize)119 bwbs_init(stream_state * st, uint osize)
120 {
121 stream_BWBS_state *const ss = (stream_BWBS_state *) st;
122 int code;
123
124 ss->bsize = ss->BlockSize;
125 code = s_buffered_block_init(st);
126 if (code != 0)
127 return code;
128 ss->offsets = (void *)gs_alloc_bytes(st->memory, osize,
129 "BWBlockSort offsets");
130 if (ss->offsets == 0) {
131 s_BWBS_release(st);
132 return ERRC;
133 /****** WRONG ******/
134 }
135 ss->I = -1; /* haven't read I yet */
136 return 0;
137 }
138
139 /* Release the filter. */
140 private void
s_BWBS_release(stream_state * st)141 s_BWBS_release(stream_state * st)
142 {
143 stream_BWBS_state *const ss = (stream_BWBS_state *) st;
144
145 gs_free_object(st->memory, ss->offsets, "BWBlockSort offsets");
146 s_buffered_release(st);
147 }
148
149 /* ------ BWBlockSortEncode ------ */
150
151 /* Initialize */
152 private int
s_BWBSE_init(stream_state * st)153 s_BWBSE_init(stream_state * st)
154 {
155 stream_BWBS_state *const ss = (stream_BWBS_state *) st;
156
157 return bwbs_init(st, ss->BlockSize * sizeof(int));
158 }
159
160 /* Compare two rotated strings for sorting. */
161 private stream_BWBS_state *bwbs_compare_ss;
162 private int
bwbs_compare_rotations(const void * p1,const void * p2)163 bwbs_compare_rotations(const void *p1, const void *p2)
164 {
165 const byte *buffer = bwbs_compare_ss->buffer;
166 const byte *s1 = buffer + *(const int *)p1;
167 const byte *s2 = buffer + *(const int *)p2;
168 const byte *start1;
169 const byte *end;
170 int swap;
171
172 if (*s1 != *s2)
173 return (*s1 < *s2 ? -1 : 1);
174 if (s1 < s2)
175 swap = 1;
176 else {
177 const byte *t = s1;
178
179 s1 = s2;
180 s2 = t;
181 swap = -1;
182 }
183 start1 = s1;
184 end = buffer + bwbs_compare_ss->N;
185 for (s1++, s2++; s2 < end; s1++, s2++)
186 if (*s1 != *s2)
187 return (*s1 < *s2 ? -swap : swap);
188 s2 = buffer;
189 for (; s1 < end; s1++, s2++)
190 if (*s1 != *s2)
191 return (*s1 < *s2 ? -swap : swap);
192 s1 = buffer;
193 for (; s1 < start1; s1++, s2++)
194 if (*s1 != *s2)
195 return (*s1 < *s2 ? -swap : swap);
196 return 0;
197 }
198 /* Sort the strings. */
199 private void
bwbse_sort(const byte * buffer,uint * indices,int N)200 bwbse_sort(const byte * buffer, uint * indices, int N)
201 {
202 offsets_full Cs;
203
204 #define C Cs.v
205 /* Sort the strings. We start with a radix sort. */
206 uint sum = 0, j, ch;
207
208 memset(C, 0, sizeof(Cs));
209 for (j = 0; j < N; j++)
210 C[buffer[j]]++;
211 for (ch = 0; ch <= 255; ch++) {
212 sum += C[ch];
213 C[ch] = sum - C[ch];
214 }
215 for (j = 0; j < N; j++)
216 indices[C[buffer[j]]++] = j;
217 /* Now C[ch] = the number of strings that start */
218 /* with a character less than or equal to ch. */
219 sum = 0;
220 /* qsort each bucket produced by the radix sort. */
221 for (ch = 0; ch <= 255; sum = C[ch], ch++)
222 qsort(indices + sum, C[ch] - sum,
223 sizeof(*indices),
224 bwbs_compare_rotations);
225 #undef C
226 }
227
228 /* Encode a buffer */
229 private int
s_BWBSE_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)230 s_BWBSE_process(stream_state * st, stream_cursor_read * pr,
231 stream_cursor_write * pw, bool last)
232 {
233 stream_BWBS_state *const ss = (stream_BWBS_state *) st;
234 register byte *q = pw->ptr;
235 byte *wlimit = pw->limit;
236 uint wcount = wlimit - q;
237 uint *indices = ss->offsets;
238
239 if (ss->filling) {
240 int status, j, N;
241 byte *buffer = ss->buffer;
242 if (wcount < sizeof(int) * 2)
243 return 1;
244
245 /* Continue filling the buffer. */
246 status = s_buffered_process(st, pr, last);
247 if (!status)
248 return 0;
249 ss->N = N = ss->bpos;
250 /* We reverse the string before encoding it, */
251 /* so it will come out of the decoder correctly. */
252 for (j = N / 2 - 1; j >= 0; j--) {
253 byte *p0 = &buffer[j];
254 byte *p1 = &buffer[N - 1 - j];
255 byte b = *p0;
256
257 *p0 = *p1;
258 *p1 = b;
259 }
260 /* Save st in a static, because that's the only way */
261 /* we can pass it to the comparison procedure (ugh). */
262 bwbs_compare_ss = ss;
263 /* Sort the strings. */
264 bwbse_sort(buffer, indices, N);
265 /* Find the unrotated string. */
266 for (j = 0; j < N; j++)
267 if (indices[j] == 0) {
268 ss->I = j;
269 break;
270 }
271 for (j = sizeof(int); --j >= 0;)
272 *++q = (byte) (N >> (j * 8));
273 for (j = sizeof(int); --j >= 0;)
274 *++q = (byte) (ss->I >> (j * 8));
275 ss->bpos = 0;
276 }
277 /* We're reading out of the buffer, writing the permuted string. */
278 while (q < wlimit && ss->bpos < ss->N) {
279 int i = indices[ss->bpos++];
280
281 *++q = ss->buffer[(i == 0 ? ss->N - 1 : i - 1)];
282 }
283 if (ss->bpos == ss->N) {
284 ss->filling = true;
285 ss->bpos = 0;
286 }
287 pw->ptr = q;
288 if (q == wlimit)
289 return 1;
290 return 0;
291 }
292
293 /* Stream template */
294 const stream_template s_BWBSE_template = {
295 &st_BWBS_state, s_BWBSE_init, s_BWBSE_process, sizeof(int) * 2, 1,
296 s_BWBS_release, s_BWBS_set_defaults
297 };
298
299 /* ------ BWBlockSortDecode ------ */
300
301 #define SHORT_OFFSETS
302
303 #ifdef SHORT_OFFSETS
304
305 /*
306 * Letting S[0..N-1] be the data block before depermutation, we need
307 * a table P[0..N-1] that maps the index i to O(S[i],i), where O(c,i) is
308 * the number of occurrences of c in S before position i.
309 * We observe that for a fixed c, O(c,i) is monotonic with i,
310 * and falls in the range 0 .. i; consequently, if 0 <= i <= j,
311 * 0 <= O(c,j) - O(c,i) <= j - i. Proceeding from this observation,
312 * rather than allocate an entire int to each entry of P,
313 * we construct three tables as follows:
314 * P2[k,c] = O(c,k*65536) for k = 0 .. (N-1)/65536;
315 * each entry requires an int.
316 * P1[k,c] = O(c,k*4096) - P2[k/16,c] for k = 0 .. (N-1)/4096;
317 * each entry falls in the range 0 .. 15*4096 and hence
318 * requires 16 bits.
319 * P0[i] = O(S[i],i) - P1[i/4096,S[i]] for i = 0 .. N-1;
320 * each entry falls in the range 0 .. 4095 and hence
321 * requires 12 bits.
322 * Since the value we need in the decompression loop is actually
323 * P[i] + C[S[i]], where C[c] is the sum of O(0,N) ... O(c-1,N),
324 * we add C[c] into P2[k,c] for all k.
325 */
326 /*typedef struct { uint v[256]; } offsets_full; *//* in sbwbs.h */
327 typedef struct {
328 bits16 v[256];
329 } offsets_4k;
330
331 #if arch_sizeof_int > 2
332 # define ceil_64k(n) (((n) + 0xffff) >> 16)
333 #else
334 # define ceil_64k(n) 1
335 #endif
336 #define ceil_4k(n) (((n) + 0xfff) >> 12)
337 #define offset_space(bsize)\
338 (ceil_64k(bsize) * sizeof(offsets_full) +\
339 ceil_4k(bsize) * sizeof(offsets_4k) +\
340 ((bsize + 1) >> 1) * 3)
341
342 #else /* !SHORT_OFFSETS */
343
344 #define offset_space(bsize)\
345 (bsize * sizeof(int))
346
347 #endif /* (!)SHORT_OFFSETS */
348
349 /* Initialize */
350 private int
s_BWBSD_init(stream_state * st)351 s_BWBSD_init(stream_state * st)
352 {
353 stream_BWBS_state *const ss = (stream_BWBS_state *) st;
354 uint bsize = ss->BlockSize;
355
356 return bwbs_init(st, offset_space(bsize));
357 }
358
359 /* Construct the decoding tables. */
360
361 #ifdef SHORT_OFFSETS
362
363 private void
bwbsd_construct_offsets(stream_BWBS_state * sst,offsets_full * po64k,offsets_4k * po4k,byte * po1,int N)364 bwbsd_construct_offsets(stream_BWBS_state * sst, offsets_full * po64k,
365 offsets_4k * po4k, byte * po1, int N)
366 {
367 offsets_full Cs;
368
369 #define C Cs.v
370 uint i1;
371 byte *b = sst->buffer;
372 offsets_full *p2 = po64k - 1;
373 offsets_4k *p1 = po4k;
374 byte *p0 = po1;
375
376 memset(C, 0, sizeof(Cs));
377 for (i1 = 0; i1 < ceil_4k(N); i1++, p1++) {
378 int j;
379
380 if (!(i1 & 15))
381 *++p2 = Cs;
382 for (j = 0; j < 256; j++)
383 p1->v[j] = C[j] - p2->v[j];
384 j = (N + 1 - (i1 << 12)) >> 1;
385 if (j > 4096 / 2)
386 j = 4096 / 2;
387 for (; j > 0; j--, b += 2, p0 += 3) {
388 byte b0 = b[0];
389 uint d0 = C[b0]++ - (p1->v[b0] + p2->v[b0]);
390 byte b1 = b[1];
391 uint d1 = C[b1]++ - (p1->v[b1] + p2->v[b1]);
392
393 p0[0] = d0 >> 4;
394 p0[1] = (byte) ((d0 << 4) + (d1 >> 8));
395 p0[2] = (byte) d1;
396 }
397 }
398 /* If the block length is odd, discount the extra byte. */
399 if (N & 1)
400 C[sst->buffer[N]]--;
401 /* Compute the cumulative totals in C. */
402 {
403 int sum = 0, ch;
404
405 for (ch = 0; ch <= 255; ch++) {
406 sum += C[ch];
407 C[ch] = sum - C[ch];
408 }
409 }
410 /* Add the C values back into the 64K table, */
411 /* which saves an addition of C[b] in the decoding loop. */
412 {
413 int i2, ch;
414
415 for (p2 = po64k, i2 = ceil_64k(N); i2 > 0; p2++, i2--)
416 for (ch = 0; ch < 256; ch++)
417 p2->v[ch] += C[ch];
418 }
419 #undef C
420 }
421
422 #else /* !SHORT_OFFSETS */
423
424 private void
bwbsd_construct_offsets(stream_BWBS_state * sst,int * po,int N)425 bwbsd_construct_offsets(stream_BWBS_state * sst, int *po, int N)
426 {
427 offsets_full Cs;
428
429 #define C Cs.v
430 uint i;
431 byte *b = sst->buffer;
432 int *p = po;
433
434 memset(C, 0, sizeof(Cs));
435 for (i = 0; i < N; i++, p++, b++)
436 *p = C[*b]++;
437 /* Compute the cumulative totals in C. */
438 {
439 int sum = 0, ch;
440
441 for (ch = 0; ch <= 255; ch++) {
442 sum += C[ch];
443 C[ch] = sum - C[ch];
444 }
445 }
446 /* Add the C values back into the offsets. */
447 for (i = 0, b = sst->buffer, p = po; i < N; i++, b++, p++)
448 *p += C[*b];
449 #undef C
450 }
451
452 #endif /* (!)SHORT_OFFSETS */
453
454 /* Decode a buffer */
455 private int
s_BWBSD_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)456 s_BWBSD_process(stream_state * st, stream_cursor_read * pr,
457 stream_cursor_write * pw, bool last)
458 {
459 stream_BWBS_state *const ss = (stream_BWBS_state *) st;
460 register const byte *p = pr->ptr;
461 const byte *rlimit = pr->limit;
462 uint count = rlimit - p;
463 register byte *q = pw->ptr;
464 byte *wlimit = pw->limit;
465
466 #ifdef SHORT_OFFSETS
467 uint BlockSize = ss->BlockSize;
468 offsets_full *po64k = ss->offsets;
469 offsets_4k *po4k = (offsets_4k *) (po64k + ceil_64k(BlockSize));
470 byte *po1 = (byte *) (po4k + ceil_4k(BlockSize));
471
472 #else /* !SHORT_OFFSETS */
473 int *po = ss->offsets;
474
475 #endif /* (!)SHORT_OFFSETS */
476
477 if (ss->I < 0) { /* Read block parameters */
478 int I, N, j;
479 if (count < sizeof(int) * 2)
480 return 0;
481 for (N = 0, j = 0; j < sizeof(int); j++)
482
483 N = (N << 8) + *++p;
484 for (I = 0, j = 0; j < sizeof(int); j++)
485
486 I = (I << 8) + *++p;
487 ss->N = N;
488 ss->I = I;
489 if_debug2('w', "[w]N=%d I=%d\n", N, I);
490 pr->ptr = p;
491 if (N < 0 || N > ss->BlockSize || I < 0 || I >= N)
492 return ERRC;
493 if (N == 0)
494 return EOFC;
495 count -= sizeof(int) * 2;
496
497 ss->bpos = 0;
498 ss->bsize = N;
499 }
500 if (ss->filling) { /* Continue filling the buffer. */
501 if (!s_buffered_process(st, pr, last))
502 return 0;
503 /* Construct the inverse sort order. */
504 #ifdef SHORT_OFFSETS
505 bwbsd_construct_offsets(ss, po64k, po4k, po1, ss->bsize);
506 #else /* !SHORT_OFFSETS */
507 bwbsd_construct_offsets(ss, po, ss->bsize);
508 #endif /* (!)SHORT_OFFSETS */
509 ss->bpos = 0;
510 ss->i = ss->I;
511 }
512 /* We're reading out of the buffer. */
513 while (q < wlimit && ss->bpos < ss->bsize) {
514 int i = ss->i;
515 byte b = ss->buffer[i];
516
517 #ifdef SHORT_OFFSETS
518 uint d;
519 const byte *pd = &po1[(i >> 1) + i];
520
521 *++q = b;
522 if (!(i & 1))
523 d = ((uint) pd[0] << 4) + (pd[1] >> 4);
524 else
525 d = ((pd[0] & 0xf) << 8) + pd[1];
526 ss->i = po64k[i >> 16].v[b] + po4k[i >> 12].v[b] + d;
527 #else /* !SHORT_OFFSETS */
528 *++q = b;
529 ss->i = po[i];
530 #endif /* (!)SHORT_OFFSETS */
531 ss->bpos++;
532 }
533 if (ss->bpos == ss->bsize) {
534 ss->I = -1;
535 ss->filling = true;
536 }
537 pw->ptr = q;
538 if (q == wlimit)
539 return 1;
540 return 0;
541 }
542
543 /* Stream template */
544 const stream_template s_BWBSD_template = {
545 &st_BWBS_state, s_BWBSD_init, s_BWBSD_process, 1, sizeof(int) * 2,
546 s_BWBS_release, s_BWBS_set_defaults
547 };
548