1 /** SR3C, a symbol ranking data compressor.
3 * This file implements a fast and effective data compressor.
4 * The compression is on par (k8: i guess ;-) to gzip -7.
5 * bzip2 -2 compresses slightly better than SR3C, but takes almost
6 * three times as long. Furthermore, since bzip2 is based on
7 * Burrows-Wheeler block sorting, it can't be used in on-line
9 * Memory consumption of SR3C is currently around 4.5 MB per ongoing
10 * compression and decompression.
12 * Author: Kenneth Oksanen <cessu@iki.fi>, 2008.
13 * Copyright (C) Helsinki University of Technology.
14 * D conversion by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
16 * This code borrows many ideas and some paragraphs of comments from
17 * Matt Mahoney's s symbol ranking compression program SR2 and Peter
18 * Fenwicks SRANK, but otherwise all code has been implemented from
21 * This file is distributed under the following license:
24 * Copyright (c) 2008 Helsinki University of Technology
25 * Permission is hereby granted, free of charge, to any person obtaining a copy
26 * of this software and associated documentation files (the "Software"), to deal
27 * in the Software without restriction, including without limitation the rights
28 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29 * copies of the Software, and to permit persons to whom the Software is
30 * furnished to do so, subject to the following conditions:
31 * The above copyright notice and this permission notice shall be included in
32 * all copies or substantial portions of the Software.
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
41 module iv
.sr3c
/*is aliced*/;
44 /** Prior to compression or uncompression the user of this library
45 * creates a "compression context" of type `SR3C` which can
46 * with certain disciplines be used for both compression and
47 * uncompression. The compression context initialization is given a
48 * function `outdg` which the compression and uncompression
49 * routines call for processing the data further.
51 * This library is MT-safe so that any number of concurrent
52 * compression or uncompression tasks may be ongoing as long as each
53 * of them is passed a distinct compression context. By default each
54 * compression context requires approximately 4.5 MB of memory, but
55 * see the internal documentation of sr3c.d for further notes on this.
57 * Compression tasks are generally NOT synchronous in the sense that
58 * once some data is passed to `compress()` the `outdg` would
59 * be passed enough compressed data so as to uncompress all of the
60 * same original data. When synchronization is required, for example
61 * at the end of successful compression, the user of this library
62 * should call `flush()`. The decompressing side will recognize
63 * the flush from the compressed data. Only when a compression and
64 * decompression have flushed in this manner, can the corresponding
65 * compression contexts be used in reverse direction.
67 public final class SR3C
{
69 /** The type of the function used to process the compressed or
70 * uncompressed data further. The function should return a non-zero
71 * value on failure. The function may not free the memory referred to
72 * by the 'bytes' argument. The argument 'flush' is passed a true
73 * value iff `flush()` was called after compressing the data.
75 alias OutputDg
= int delegate (const(void)[] bytes
, bool flush
);
78 enum SR_HMASK
= 0xFFFFF;
81 /* It is possible to reduce memory consumption from 4.5 MB to, say,
82 1.5 MB with the following definitions, but this will also reduce
83 the compression ratio. In my tests on average this would result in
84 an almost 4% increase in the size of the compressed data.
85 #define SR_HMASK 0x3FFFF
88 Even further memory savings are possible, but again with worse
89 compression. The following definitions require some 0.75 MB memory
90 and result 7-8% larger compressed data than with current default
91 definitions. Further memory savings would require changes in the
92 secondary arithmetic compression, a.k.a. state maps.
93 #define SR_HMASK 0xFFFF
96 Conversely, by allowing a loftier memory budget, say 64.5 MB,
97 compression can be improved further. The following will result in
98 a little over 2% improvement in compression:
99 #define SR_HMASK 0xFFFFFF
104 enum SM_P_HALF
= 1U<<31;
106 enum SM_HUFF_LO_N
= 4*256*3;
107 enum SM_HUFF_HI_N1
= 16;
108 enum SM_HUFF_HI_N2
= 3;
109 enum SM_BYTES_N
= 256*256;
114 /* The following states are for the uncompressor. */
115 SR_FILLING_1
, SR_FILLING_2
, SR_FILLING_3
,
116 SR_UNCOMPRESSING_1
, SR_UNCOMPRESSING_2
, SR_UNCOMPRESSING_3
,
117 SR_UNCOMPRESSING_BYTE
, SR_FLUSHING
,
120 ubyte* outbuf_p
; // current output buffer
121 uint[SR_HMASK
+1] rank
; // symbol ranking tables
122 uint hash
; // hash value of the current compression context
123 int prev_ch
; // previous byte we encoded, 0 if none
124 /* statemaps map secondary context to probability.
125 * each statemap entry contains the prediction in the upper 25 bits and a count of
126 * how many times this state has been reached in the lower 7 bits. */
127 // states for encoding the first three bits in each context; some of the entries in sm_huff_hi are unused
128 uint[SM_HUFF_LO_N
] sm_huff_lo
;
129 uint[SM_HUFF_HI_N2
][SM_HUFF_HI_N1
] sm_huff_hi
;
130 // states for encoding a byte not predicted by symbol ranker using a context-1 arithmetic encoding
131 uint[SM_BYTES_N
] sm_bytes
;
132 // arithmetic coder range, initially [0, 1), scaled by 2^32; the field x is use only during uncompression
134 // iutput function and its context, returns non-zero on error
136 int status
; // SR_XXX
137 // the following field is used by the uncompressor while uncompressing literal bytes
141 this (OutputDg outdg
) { reset(outdg
); }
144 /** Reinitialize the given compression context. If `outdg` is
145 * non-null, it is assigned to the compression context's new output
146 * function. If, on the other hand, output_f is `null` the callback
147 * function remain as it is.
149 public void reset (OutputDg outdg
=null) {
151 /* Initialize statemaps. The initial values were chosen based on a
152 large number of test runs. The cumulative statistic match quite
153 well those of Fenwick:
154 - Least recently used matches with 45% probability (over all counts),
155 - Second least recently used matches with 15% probability,
156 - Third least recently used matches with 7% probability,
157 - Literals match with 32% probability.
158 Initializing to anything else but SM_P_HALF produces
159 proportionally modest benefits for large inputs, but we wanted
160 SR3C to be effective also for small inputs. */
161 for (i
= 0; i
< 3*256; i
+= 3) {
162 sm_huff_lo
.ptr
[i
] = (3400<<20)|
0;
163 sm_huff_lo
.ptr
[i
+1] = (150<<20)|
12;
164 sm_huff_lo
.ptr
[i
+2] = (1000<<20)|
12;
166 for (; i
< 2*3*256; i
+= 3) {
167 sm_huff_lo
.ptr
[i
] = (1500<<20)|
0;
168 sm_huff_lo
.ptr
[i
+1] = (1840<<20)|
12;
169 sm_huff_lo
.ptr
[i
+2] = (780<<20)|
12;
171 for (; i
< 3*3*256; i
+= 3) {
172 sm_huff_lo
.ptr
[i
] = (880<<20)|
0;
173 sm_huff_lo
.ptr
[i
+1] = (1840<<20)|
12;
174 sm_huff_lo
.ptr
[i
+2] = (760<<20)|
12;
176 for (; i
< 4*3*256; i
+= 3) {
177 sm_huff_lo
.ptr
[i
] = (780<<20)|
0;
178 sm_huff_lo
.ptr
[i
+1] = (1840<<20)|
12;
179 sm_huff_lo
.ptr
[i
+2] = (1120<<20)|
12;
181 for (i
= 0; i
< SM_HUFF_HI_N1
; i
++) {
182 sm_huff_hi
.ptr
[i
].ptr
[0] = (400<<20)|
10;
183 sm_huff_hi
.ptr
[i
].ptr
[1] = (1840<<20)|
12;
184 sm_huff_hi
.ptr
[i
].ptr
[2] = (1180<<20)|
12;
186 for (i
= 0; i
< SM_BYTES_N
; i
++)
187 sm_bytes
.ptr
[i
] = SM_P_HALF
;
189 for (i
= 0; i
< SR_HMASK
+1; i
++)
197 if (outdg
!is null) output_f
= outdg
;
201 // what is the prediction, as a fractional probability from 0 to 4095, of the next bit being one
202 enum SM_PREDICT(string sm
) = "(("~sm
~")>>20)";
203 enum SM_COUNT(string sm
) = "(("~sm
~")&0x1FF)";
206 enum SM_UPDATE_RANKS(string sm
, string bit
) = "do {\n"~
207 //"assert(bit == 0 || bit == 1);\n"~
208 "int count = "~sm
~"&0x1FF, prediction__ = "~sm
~">>9;\n"~
209 "int d = ("~bit
~"<<23)-prediction__;\n"~
210 ""~sm
~" += (d*sr_wt_ranks.ptr[count])&0xFFFFFE00;\n"~
211 "if (d < 0) d = -d;\n"~
213 "//"~sm
~" += sr_ct_ranks.ptr[d].ptr[count];\n"~
214 ""~sm
~" ^= sr_ct_ranks.ptr[d].ptr[count];\n"~
217 enum SM_UPDATE_BYTES(string sm
, string bit
) = "do {\n"~
218 //"assert(bit == 0 || bit == 1);\n"~
219 "int count = "~sm
~"&0x1FF, prediction__ = "~sm
~">>9;\n"~
220 "int d = ("~bit
~"<<23)-prediction__;\n"~
221 ""~sm
~" += (d*sr_wt_bytes.ptr[count])&0xFFFFFE00;\n"~
222 "if (d < 0) d = -d;\n"~
224 ""~sm
~" ^= sr_ct_bytes.ptr[d].ptr[count];\n"~
227 // compress bit in the given state map, possibly outputting bytes in process
228 enum SR_ENCODE_RANK_BIT(string sm
, string bit
) = "do {\n"~
230 "int prediction_ = "~SM_PREDICT
!sm
~";\n"~
231 //"assert(bit == 0 || bit == 1);\n"~
232 "assert(prediction_ >= 0 && prediction_ < 4096);\n"~
233 "xmid = x1+((x2-x1)>>12)*prediction_;\n"~
234 "assert(xmid >= x1 && xmid < x2);\n"~
235 "if ("~bit
~") x2 = xmid; else x1 = xmid+1;\n"~
236 SM_UPDATE_RANKS
!(sm
, bit
)~"\n"~
237 "/* pass equal leading bytes of range */\n"~
238 "while ((x1>>24) == (x2>>24)) {\n"~
239 " *outbuf_p++ = x2>>24;\n"~
241 " x2 = (x2<<8)+255;\n"~
245 enum SR_ENCODE_BYTE_BIT(string sm
, string bit
) = "do {\n"~
247 "int prediction_ = "~SM_PREDICT
!sm
~";\n"~
248 //"assert(bit == 0 || bit == 1);\n"~
249 "assert(prediction_ >= 0 && prediction_ < 4096);\n"~
250 "xmid = x1+((x2-x1)>>12)*prediction_;\n"~
251 "assert(xmid >= x1 && xmid < x2);\n"~
252 "if ("~bit
~") x2 = xmid; else x1 = xmid+1;\n"~
253 SM_UPDATE_BYTES
!(sm
, bit
)~"\n"~
254 "// pass equal leading bytes of range\n"~
255 "while ((x1>>24) == (x2>>24)) {\n"~
256 " *outbuf_p++ = x2>>24;\n"~
258 " x2 = (x2<<8)+255;\n"~
262 enum SR_ENCODE_BYTE(string ch
) = "do {\n"~
263 "uint* sm_ = &sm_bytes.ptr[256*prev_ch];\n"~
264 "uint ix = 1, x = "~ch
~", bit;\n"~
266 " bit = (x>>7)&0x1;\n"~
267 " "~SR_ENCODE_BYTE_BIT
!("sm_[ix]", "bit")~"\n"~
268 " ix = (ix<<1)|bit;\n"~
270 "} while (ix < 256);\n"~
274 enum OUTBUF_SIZE
= 1024;
276 /** Compress the given bytes, possibly sending some compressed data to
277 * `outdg`. Returns zero on success, or whatever `outdg` returned
278 * should it have erred. Once the context has been used for
279 * compressing, it can't be used for uncompressing before `flush()`
282 public int compress (const(void)[] data
) {
283 const(ubyte)* bytes
= cast(const(ubyte)*)data
.ptr
;
284 usize n_bytes
= data
.length
;
288 ubyte[OUTBUF_SIZE
] outbuf
;
289 /* The theoretical worst case is that each bit is encoded into 12
290 bits, and there can be 10 bits of output per bit sent to the
291 arithmetic coder, or 15 bytes. Additionally there may be some
292 bytes of flushing from the arithmetic coder itself, thus a margin
293 of slightly cautious 30 bytes. */
294 ubyte* outbuf_end
= outbuf
.ptr
+OUTBUF_SIZE
-30;
296 assert(status
== SR_FLUSHED || status
== SR_COMPRESSING
);
298 status
= SR_COMPRESSING
;
300 while (n_bytes
> 0) {
301 outbuf_p
= outbuf
.ptr
;
302 while (outbuf_p
< outbuf_end
&& n_bytes
> 0) {
304 index
= hash
&SR_HMASK
;
305 xsum
= (hash
>>SR_XSHFT
)&0xF;
307 if (((r
>>24)&0xF) != xsum
) {
308 // hash collision: pick another index, use it if checksums match or it has a lower first-hit counter value
309 int alt_index
= (index
+0x3A77)&SR_HMASK
;
310 uint alt_r
= rank
.ptr
[alt_index
];
311 if (((alt_r
>>24)&0xF) == xsum || alt_r
< r
) {
317 sm
= &sm_huff_hi
.ptr
[r
>>28].ptr
[0];
319 sm
= &sm_huff_lo
.ptr
[3*(prev_ch|
((r
>>20)&0x300))];
322 xsum
= (xsum
<<24)|ch
;
323 if (ch
== (r
&0xFF)) {
324 // is ch the least recently seen?
325 mixin(SR_ENCODE_RANK_BIT
!("sm[0]", "0"));
326 if (r
< 0xF0000000) r
+= 0x10000000; // increment hit count
328 mixin(SR_ENCODE_RANK_BIT
!("sm[0]", "1"));
329 if (ch
== ((r
>>8)&0xFF)) {
330 // is ch the second least recent?
331 mixin(SR_ENCODE_RANK_BIT
!("sm[1]", "1"));
332 mixin(SR_ENCODE_RANK_BIT
!("sm[2]", "0"));
336 r
= (r
&0xFF0000)|
((r
&0xFF)<<8)|xsum|
0x10000000;
337 } else if (ch
== ((r
>>16)&0xFF)) {
338 // is ch the third least recent?
339 mixin(SR_ENCODE_RANK_BIT
!("sm[1]", "1"));
340 mixin(SR_ENCODE_RANK_BIT
!("sm[2]", "1"));
341 r
= ((r
&0xFFFF)<<8)|xsum|
0x10000000;
343 mixin(SR_ENCODE_RANK_BIT
!("sm[1]", "0"));
344 mixin(SR_ENCODE_BYTE
!"ch");
345 r
= ((r
&0xFFFF)<<8)|xsum
;
350 hash
= SR_HMULT
*hash
+ch
+1;
352 if (outbuf_p
> outbuf
.ptr
) {
353 if (int err
= output_f(outbuf
[0..outbuf_p
-outbuf
.ptr
], false)) return err
;
359 /** Flush the internal buffered state of the compression context to
360 * `outdg` so that a uncompression of all the data provided prior
361 * to the flush becomes possible. Returns zero on success, or
362 * whatever `outdg` returned should it have erred. It is possible
363 * to continue compression after flushing the buffers, but each flush
364 * will cause at least a three byte overhead, probably higher.
365 * File compressors and archivers typically flush only at the end of
366 * compression, but message-interchanging programs flush whenever
367 * real-timeness requires or a response is required to proceed.
369 * Note that `flush()` may not be called for a context currently used
372 public int flush () {
375 int index
, xsum
, i
, err
;
377 outbuf_p
= &outbuf
[0];
379 assert(status
== SR_COMPRESSING || status
== SR_FLUSHED
);
381 /* Pick state map entry as if compressing a normal data byte. */
382 index
= hash
&SR_HMASK
;
383 xsum
= (hash
>>SR_XSHFT
)&0xF;
385 if (((r
>>24)&0xF) != xsum
) {
386 int alt_index
= (index
+0x3A77)&SR_HMASK
;
387 uint alt_r
= rank
.ptr
[alt_index
];
388 if (((alt_r
>>24)&0xF) == xsum || alt_r
< r
) {
394 sm
= &sm_huff_hi
.ptr
[r
>>28].ptr
[0];
396 sm
= &sm_huff_lo
.ptr
[3*(prev_ch|
((r
>>20)&0x300))];
398 /* Mark end of data by coding third least recently used byte as
400 mixin(SR_ENCODE_RANK_BIT
!("sm[0]", "1"));
401 mixin(SR_ENCODE_RANK_BIT
!("sm[1]", "0"));
402 mixin(SR_ENCODE_BYTE
!"((r>>16)&0xFF)");
404 /* Flush also the arithmetic encoder, first by the first unequal
405 byte in the range and thereafter three maximum bytes. */
406 *outbuf_p
++ = x1
>>24;
411 /* Finally send this all out. */
412 err
= output_f(outbuf
[0..outbuf_p
-outbuf
.ptr
], true);
415 /* Reset internal values in the context, not however the statemaps or ranks. */
425 /** Return true if the given context is flushed, i.e. if the
426 * context can now be used for either compression or uncompression.
428 public @property bool flushed () const pure nothrow @safe @nogc { return (status
== SR_FLUSHED
); }
430 /** Uncompress the given bytes, possibly sending some uncompressed data
431 * to `outdg`. Returns zero on success, or whatever `outdg`
432 * returned should it have failed. SR3C includes no checksum so
433 * corrupted compressed messages will not be detected. Once the
434 * context has been used for uncompressing, it can't be used for
435 * compressing before `flush()` has been issued on the corresponding
436 * compressing side and the resulting compressed data has been
439 public int uncompress (const(void)[] data
) {
440 enum SR_INPUT(string state_name
) =
443 " while ((x1>>24) == (x2>>24)) {\n"~
444 " if (n_bytes == 0) { status = "~state_name
~"; goto out_of_input; }\n"~
446 " x2 = (x2<<8)+255;\n"~
447 " x = (x<<8)+*bytes++;\n"~
452 enum SR_DECODE_BIT(string sm
, string bit
, string state_name
) =
454 " "~SR_INPUT
!(state_name
)~"\n"~
455 " prediction = "~SM_PREDICT
!(sm
)~";\n"~
456 " assert(prediction >= 0 && prediction < 4096);\n"~
457 " xmid = x1+((x2-x1)>>12)*prediction;\n"~
458 " assert(xmid >= x1 && xmid < x2);\n"~
459 " if (x <= xmid) {\n"~
468 const(ubyte)* bytes
= cast(const(ubyte)*)data
.ptr
;
469 usize n_bytes
= data
.length
;
476 ubyte[OUTBUF_SIZE
] outbuf
;
477 outbuf_p
= outbuf
.ptr
;
478 ubyte* outbuf_end
= outbuf
.ptr
+OUTBUF_SIZE
;
480 assert(status
!= SR_COMPRESSING
);
484 while (n_bytes
> 0 && *bytes
== 0xFF) {
488 if (n_bytes
-- == 0) return 0;
493 if (n_bytes
-- == 0) {
494 status
= SR_FILLING_1
;
500 if (n_bytes
-- == 0) {
501 status
= SR_FILLING_2
;
507 if (n_bytes
-- == 0) {
508 status
= SR_FILLING_3
;
512 status
= SR_UNCOMPRESSING_1
;
517 // the default branch is here to only to keep the compiler happy
521 index
= hash
&SR_HMASK
;
522 xsum
= (hash
>>SR_XSHFT
)&0xF;
524 if (((r
>>24)&0xF) != xsum
) {
525 // hash collision: pick another index, use it if checksums match or it has a lower first-hit counter value
526 int alt_index
= (index
+0x3A77)&SR_HMASK
;
527 uint alt_r
= rank
.ptr
[alt_index
];
528 if (((alt_r
>>24)&0xF) == xsum || alt_r
< r
) {
534 sm
= &sm_huff_hi
.ptr
[r
>>28].ptr
[0];
536 sm
= &sm_huff_lo
.ptr
[3*(prev_ch|
((r
>>20)&0x300))];
540 case SR_UNCOMPRESSING_1
: goto SR_UNCOMPRESSING_1
;
541 case SR_UNCOMPRESSING_2
: goto SR_UNCOMPRESSING_2
;
542 case SR_UNCOMPRESSING_3
: goto SR_UNCOMPRESSING_3
;
543 case SR_UNCOMPRESSING_BYTE
: sm
= &sm_bytes
.ptr
[256*prev_ch
]; goto SR_UNCOMPRESSING_BYTE
;
548 index
= hash
&SR_HMASK
;
549 xsum
= (hash
>>SR_XSHFT
)&0xF;
551 if (((r
>>24)&0xF) != xsum
) {
552 // hash collision: pick another index, use it if checksums match or it has a lower first-hit counter value
553 int alt_index
= (index
+0x3A77)&SR_HMASK
;
554 uint alt_r
= rank
.ptr
[alt_index
];
555 if (((alt_r
>>24)&0xF) == xsum || alt_r
< r
) {
561 sm
= &sm_huff_hi
.ptr
[r
>>28].ptr
[0];
563 sm
= &sm_huff_lo
.ptr
[3*(prev_ch|
((r
>>20)&0x300))];
566 mixin(SR_DECODE_BIT
!("sm[0]", "bit", "SR_UNCOMPRESSING_1"));
567 mixin(SM_UPDATE_RANKS
!("sm[0]", "bit"));
569 mixin(SR_DECODE_BIT
!("sm[1]", "bit", "SR_UNCOMPRESSING_2"));
570 mixin(SM_UPDATE_RANKS
!("sm[1]", "bit"));
572 mixin(SR_DECODE_BIT
!("sm[2]", "bit", "SR_UNCOMPRESSING_3"));
573 mixin(SM_UPDATE_RANKS
!("sm[2]", "bit"));
575 // third least recent byte
577 r
= ((r
&0xFFFF)<<8)|ch|xsum|
0x10000000;
579 // second least recent byte
584 r
= (r
&0xFF0000)|
((r
&0xFF)<<8)|ch|xsum|
0x10000000;
587 sm
= &sm_bytes
.ptr
[256*prev_ch
];
590 mixin(SR_DECODE_BIT
!("sm[lit_ix]", "bit", "SR_UNCOMPRESSING_BYTE"));
591 mixin(SM_UPDATE_BYTES
!("sm[lit_ix]", "bit"));
592 lit_ix
= (lit_ix
<<1)|bit
;
593 } while (lit_ix
< 256);
595 if (ch
== ((r
>>16)&0xFF)) goto flush
;
596 r
= ((r
&0xFFFF)<<8)|ch|xsum
;
601 if (r
< 0xF0000000) r
+= 0x10000000;
604 *outbuf_p
++ = cast(ubyte)ch
;
607 hash
= SR_HMULT
*hash
+ch
+1;
608 if (outbuf_p
== outbuf_end
) {
609 err
= output_f(outbuf
[0..OUTBUF_SIZE
], false);
611 outbuf_p
= outbuf
.ptr
;
616 // we come here when we have received a flush.
617 // pass the flush-induced bytes in the data stream and reset internal values
618 // in the context, not however the statemaps or ranks.
619 mixin(SR_INPUT
!"SR_FLUSHING");
625 /* Skip 0xFF-bytes. */
626 while (n_bytes
> 0 && *bytes
== 0xFF) {
630 err
= output_f(outbuf
[0..outbuf_p
-outbuf
.ptr
], true);
632 outbuf_p
= outbuf
.ptr
;
634 outbuf_p
= outbuf
.ptr
;
640 if (outbuf_p
!= outbuf
.ptr
) return output_f(outbuf
[0..outbuf_p
-outbuf
.ptr
], false);
645 // some precomputed tables on how the secondary arithmetical compressor adjusts to actual data; see comments later
646 static immutable ubyte[512] sr_wt_bytes
;
647 static immutable ubyte[512] sr_wt_ranks
;
648 static immutable ushort[512][65] sr_ct_bytes
;
649 static immutable ushort[512][65] sr_ct_ranks
;
651 shared static this () pure nothrow @safe @nogc {
652 // initialize the various precomputed tables
653 foreach (int count
; 0..512) {
654 // a table indexed by current state map count and returning the corresponding responsiveness to unpredicted input
655 // found with numeric optimization for the order-0 arithmetic compressor
656 sr_wt_bytes
[count
] = cast(ubyte)(2.814086+(1.107489*count
+639.588922)/(0.006940*count
*count
+count
+6.318012));
657 // as above, but for rank encoding data
658 sr_wt_ranks
[count
] = cast(ubyte)(-1.311630+(0.616477*count
+640.391038)/(0.001946*count
*count
+count
+5.632143));
659 foreach (int d
; 0..64+1) {
660 // a table for updating the count-part in statemaps for bytes
661 int c
= (d
> 1.1898325 && count
> 19.782085 ?
cast(int)(-2+0.021466*(d
-1.1898325)*(count
-19.782085)) : -2);
662 if (c
> count
) c
= 0; else if (count
-c
> 0x1FF) c
= 0x1FF; else c
= count
-c
;
663 sr_ct_bytes
[d
][count
] = cast(ushort)(c^count
);
664 // a table for updating the count-part in statemaps for ranks
665 c
= (count
> 33.861341 ?
cast(int)(-2+0.005355*(d
+0.981405)*(count
-33.861341)) : -2);
666 if (c
> count
) c
= 0; else if (count
-c
> 0x1FF) c
= 0x1FF; else c
= count
-c
;
667 sr_ct_ranks
[d
][count
] = cast(ushort)(c^count
);
674 /* This code is based on many of the ideas in Matt Mahoney's SR2
675 symbol ranking compression program
676 http://www.cs.fit.edu/~mmahoney/compression/#sr2
677 which in turn is based on SRANK by P. M. Fenwick in 1997-98
678 ftp://ftp.cs.auckland.ac.nz/pub/staff/peter-f/srank.c
679 See also Fenwick's technical report "A fast, constant-order, symbol
680 ranking text compressor", The University of Auckland, Department of
681 Computer Science Report No 145, Apr. 1997.
683 A symbol ranking compressor maintains a list (a move-to-front
684 queue) of the most recently seen symbols (bytes) in the given
685 context in order of time since last seen. The original SRANK used
686 a table of 2^10 to 2^18 hashed order-3 contexts, SR2 uses 2^20
687 hashed order-4 contexts, and some versions of SR3 use 2^24
688 contexts, which we considered excessively much in terms of implied
689 memory consumption. All the mentioned programs use a queue length
690 of 3, SR2 furthermore uses a 6 bit count (n) of consecutive hits,
691 SR3C uses only a 4 bit count but employs a 4-bit checksum to reduce
694 SR2 as well as SR3C follow Fenwick's suggestion for a hardware
695 implementation in sending 0 for literals, 110 and 111 for the
696 second and third least recently seen, and 10xxxxxxxx's are reserved
697 for literals. These are compressed further with arithmetic coding
698 using both an order-1 context (last coded byte) and the count as
699 context, or order-0 and count if the count is greater or equal to
702 Codes and updates are as follows:
704 Input Code Next state
706 ----- ---- ---------------
708 c1 0 (c1, c2, c3, min(n+1, 15))
709 c2 110 (c2, c1, c3, 1)
710 c3 111 (c3, c1, c2, 1)
711 other c 10cccccccc (c, c1, c2, 0)
713 As an exception, however, in SR3C if input is c2 and n has reached
714 very high counts (12 or more), then the count is reduced to four
715 but the queue is kept intact.
717 After coding byte c, the hash index h is updated to h * 480 + c + 1
718 (mod 2^20) which depends on only the last 4 bytes. SR2 did not
719 detect hash collisions, but SR3C uses a 4-bit checksum which is
720 formed by the next four higher bits of the hash. The values are
721 packed into a 32 bit integer as follows: c1 in bits 0-7, c2 in
722 8-15, c3 in 16-23, checksum in 24-27, n in 28-31.
724 End of file is marked by coding c3 as a literal (SR2 used c1)
725 followed by three 0xFF's. The compressor library adds no header,
726 but ensures the message doesn't begin with 0xFF so as to enable
727 arbitrary catenation of messages. Additional headers and checksums
728 may be added by a stand-alone archiver using this library.
730 Arithmetic coding is performed as in SR2, and we advise the reader
731 to its documentation. Let it only be noted that compared to SR2,
732 SR3C uses far fewer arithmetic coding states than SR2 thus saving
733 ~1.5 MB of RAM as well as incidentally also a slightly better
734 compression ratio. There are also several other differences in
735 between SR2 and SR3C in how the predictions for next bits are