File Coverage

blib/lib/Image/ExifTool/BZZ.pm
Criterion Covered Total %
statement 188 204 92.1
branch 41 62 66.1
condition 8 20 40.0
subroutine 15 15 100.0
pod 0 8 0.0
total 252 309 81.5


line stmt bran cond sub pod time code
1             #------------------------------------------------------------------------------
2             # File: BZZ.pm
3             #
4             # Description: Utility to decode BZZ compressed data
5             #
6             # Revisions: 09/22/2008 - P. Harvey Created
7             #
8             # References: 1) http://djvu.sourceforge.net/
9             # 2) http://www.djvu.org/
10             #
11             # Notes: This code based on ZPCodec and BSByteStream of DjVuLibre 3.5.21
12             # (see NOTES documentation below for license/copyright details)
13             #------------------------------------------------------------------------------
14              
15             package Image::ExifTool::BZZ;
16              
17 1     1   7 use strict;
  1         2  
  1         33  
18 1     1   584 use integer; # IMPORTANT!! use integer arithmetic throughout
  1         14  
  1         6  
19             require Exporter;
20 1     1   40 use vars qw($VERSION @ISA @EXPORT_OK);
  1         2  
  1         1324  
21              
22             $VERSION = '1.00';
23             @ISA = qw(Exporter);
24             @EXPORT_OK = qw(Decode);
25              
26             # constants
27 16912     16912 0 23857 sub FREQMAX { 4 }
28 3256     3256 0 4549 sub CTXIDS { 3 }
29 1     1 0 5 sub MAXBLOCK { 4096 }
30              
31             # This table has been designed for the ZPCoder
32             # by running the following command in file 'zptable.sn':
33             # (fast-crude (steady-mat 0.0035 0.0002) 260)))
34             my @default_ztable_p = (
35             0x8000, 0x8000, 0x8000, 0x6bbd, 0x6bbd, 0x5d45, 0x5d45, 0x51b9, 0x51b9, 0x4813,
36             0x4813, 0x3fd5, 0x3fd5, 0x38b1, 0x38b1, 0x3275, 0x3275, 0x2cfd, 0x2cfd, 0x2825,
37             0x2825, 0x23ab, 0x23ab, 0x1f87, 0x1f87, 0x1bbb, 0x1bbb, 0x1845, 0x1845, 0x1523,
38             0x1523, 0x1253, 0x1253, 0x0fcf, 0x0fcf, 0x0d95, 0x0d95, 0x0b9d, 0x0b9d, 0x09e3,
39             0x09e3, 0x0861, 0x0861, 0x0711, 0x0711, 0x05f1, 0x05f1, 0x04f9, 0x04f9, 0x0425,
40             0x0425, 0x0371, 0x0371, 0x02d9, 0x02d9, 0x0259, 0x0259, 0x01ed, 0x01ed, 0x0193,
41             0x0193, 0x0149, 0x0149, 0x010b, 0x010b, 0x00d5, 0x00d5, 0x00a5, 0x00a5, 0x007b,
42             0x007b, 0x0057, 0x0057, 0x003b, 0x003b, 0x0023, 0x0023, 0x0013, 0x0013, 0x0007,
43             0x0007, 0x0001, 0x0001, 0x5695, 0x24ee, 0x8000, 0x0d30, 0x481a, 0x0481, 0x3579,
44             0x017a, 0x24ef, 0x007b, 0x1978, 0x0028, 0x10ca, 0x000d, 0x0b5d, 0x0034, 0x078a,
45             0x00a0, 0x050f, 0x0117, 0x0358, 0x01ea, 0x0234, 0x0144, 0x0173, 0x0234, 0x00f5,
46             0x0353, 0x00a1, 0x05c5, 0x011a, 0x03cf, 0x01aa, 0x0285, 0x0286, 0x01ab, 0x03d3,
47             0x011a, 0x05c5, 0x00ba, 0x08ad, 0x007a, 0x0ccc, 0x01eb, 0x1302, 0x02e6, 0x1b81,
48             0x045e, 0x24ef, 0x0690, 0x2865, 0x09de, 0x3987, 0x0dc8, 0x2c99, 0x10ca, 0x3b5f,
49             0x0b5d, 0x5695, 0x078a, 0x8000, 0x050f, 0x24ee, 0x0358, 0x0d30, 0x0234, 0x0481,
50             0x0173, 0x017a, 0x00f5, 0x007b, 0x00a1, 0x0028, 0x011a, 0x000d, 0x01aa, 0x0034,
51             0x0286, 0x00a0, 0x03d3, 0x0117, 0x05c5, 0x01ea, 0x08ad, 0x0144, 0x0ccc, 0x0234,
52             0x1302, 0x0353, 0x1b81, 0x05c5, 0x24ef, 0x03cf, 0x2b74, 0x0285, 0x201d, 0x01ab,
53             0x1715, 0x011a, 0x0fb7, 0x00ba, 0x0a67, 0x01eb, 0x06e7, 0x02e6, 0x0496, 0x045e,
54             0x030d, 0x0690, 0x0206, 0x09de, 0x0155, 0x0dc8, 0x00e1, 0x2b74, 0x0094, 0x201d,
55             0x0188, 0x1715, 0x0252, 0x0fb7, 0x0383, 0x0a67, 0x0547, 0x06e7, 0x07e2, 0x0496,
56             0x0bc0, 0x030d, 0x1178, 0x0206, 0x19da, 0x0155, 0x24ef, 0x00e1, 0x320e, 0x0094,
57             0x432a, 0x0188, 0x447d, 0x0252, 0x5ece, 0x0383, 0x8000, 0x0547, 0x481a, 0x07e2,
58             0x3579, 0x0bc0, 0x24ef, 0x1178, 0x1978, 0x19da, 0x2865, 0x24ef, 0x3987, 0x320e,
59             0x2c99, 0x432a, 0x3b5f, 0x447d, 0x5695, 0x5ece, 0x8000, 0x8000, 0x5695, 0x481a,
60             0x481a, 0, 0, 0, 0, 0
61             );
62             my @default_ztable_m = (
63             0x0000, 0x0000, 0x0000, 0x10a5, 0x10a5, 0x1f28, 0x1f28, 0x2bd3, 0x2bd3, 0x36e3,
64             0x36e3, 0x408c, 0x408c, 0x48fd, 0x48fd, 0x505d, 0x505d, 0x56d0, 0x56d0, 0x5c71,
65             0x5c71, 0x615b, 0x615b, 0x65a5, 0x65a5, 0x6962, 0x6962, 0x6ca2, 0x6ca2, 0x6f74,
66             0x6f74, 0x71e6, 0x71e6, 0x7404, 0x7404, 0x75d6, 0x75d6, 0x7768, 0x7768, 0x78c2,
67             0x78c2, 0x79ea, 0x79ea, 0x7ae7, 0x7ae7, 0x7bbe, 0x7bbe, 0x7c75, 0x7c75, 0x7d0f,
68             0x7d0f, 0x7d91, 0x7d91, 0x7dfe, 0x7dfe, 0x7e5a, 0x7e5a, 0x7ea6, 0x7ea6, 0x7ee6,
69             0x7ee6, 0x7f1a, 0x7f1a, 0x7f45, 0x7f45, 0x7f6b, 0x7f6b, 0x7f8d, 0x7f8d, 0x7faa,
70             0x7faa, 0x7fc3, 0x7fc3, 0x7fd7, 0x7fd7, 0x7fe7, 0x7fe7, 0x7ff2, 0x7ff2, 0x7ffa,
71             0x7ffa, 0x7fff, 0x7fff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
72             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
73             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
74             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
75             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
76             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
77             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
78             );
79             my @default_ztable_up = (
80             84, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
81             18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
82             34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
83             50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
84             66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
85             82, 81, 82, 9, 86, 5, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
86             82, 99, 76, 101, 70, 103, 66, 105, 106, 107, 66, 109, 60, 111, 56, 69,
87             114, 65, 116, 61, 118, 57, 120, 53, 122, 49, 124, 43, 72, 39, 60, 33,
88             56, 29, 52, 23, 48, 23, 42, 137, 38, 21, 140, 15, 142, 9, 144, 141,
89             146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 70, 157, 66, 81, 62, 75,
90             58, 69, 54, 65, 50, 167, 44, 65, 40, 59, 34, 55, 30, 175, 24, 177,
91             178, 179, 180, 181, 182, 183, 184, 69, 186, 59, 188, 55, 190, 51, 192, 47,
92             194, 41, 196, 37, 198, 199, 72, 201, 62, 203, 58, 205, 54, 207, 50, 209,
93             46, 211, 40, 213, 36, 215, 30, 217, 26, 219, 20, 71, 14, 61, 14, 57,
94             8, 53, 228, 49, 230, 45, 232, 39, 234, 35, 138, 29, 24, 25, 240, 19,
95             22, 13, 16, 13, 10, 7, 244, 249, 10, 89, 230, 0, 0, 0, 0, 0
96             );
97             my @default_ztable_dn = (
98             145, 4, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
99             14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
100             30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
101             46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
102             62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
103             78, 79, 80, 85, 226, 6, 176, 143, 138, 141, 112, 135, 104, 133, 100, 129,
104             98, 127, 72, 125, 102, 123, 60, 121, 110, 119, 108, 117, 54, 115, 48, 113,
105             134, 59, 132, 55, 130, 51, 128, 47, 126, 41, 62, 37, 66, 31, 54, 25,
106             50, 131, 46, 17, 40, 15, 136, 7, 32, 139, 172, 9, 170, 85, 168, 248,
107             166, 247, 164, 197, 162, 95, 160, 173, 158, 165, 156, 161, 60, 159, 56, 71,
108             52, 163, 48, 59, 42, 171, 38, 169, 32, 53, 26, 47, 174, 193, 18, 191,
109             222, 189, 218, 187, 216, 185, 214, 61, 212, 53, 210, 49, 208, 45, 206, 39,
110             204, 195, 202, 31, 200, 243, 64, 239, 56, 237, 52, 235, 48, 233, 44, 231,
111             38, 229, 34, 227, 28, 225, 22, 223, 16, 221, 220, 63, 8, 55, 224, 51,
112             2, 47, 87, 43, 246, 37, 244, 33, 238, 27, 236, 21, 16, 15, 8, 241,
113             242, 7, 10, 245, 2, 1, 83, 250, 2, 143, 246, 0, 0, 0, 0, 0
114             );
115              
116             #------------------------------------------------------------------------------
117             # New - create new BZZ object
118             # Inputs: 0) reference to BZZ object or BZZ class name
119             # Returns: blessed BZZ object ref
120             sub new
121             {
122 1     1 0 1 local $_;
123 1         3 my $that = shift;
124 1   50     7 my $class = ref($that) || $that || 'Image::ExifTool::BZZ';
125 1         4 return bless {}, $class;
126             }
127              
128             #------------------------------------------------------------------------------
129             # Initialize BZZ object
130             # Inputs: 0) BZZ object ref, 1) data ref, 2) true for DjVu compatibility
131             sub Init($$)
132             {
133 1     1 0 4 my ($self, $dataPt, $djvucompat) = @_;
134             # Create machine independent ffz table
135 1         9 my $ffzt = $$self{ffzt} = [ ];
136 1         2 my ($i, $j);
137 1         7 for ($i=0; $i<256; $i++) {
138 256         282 $$ffzt[$i] = 0;
139 256         413 for ($j=$i; $j&0x80; $j<<=1) {
140 255         400 $$ffzt[$i] += 1;
141             }
142             }
143             # Initialize table
144 1         15 $$self{p} = [ @default_ztable_p ];
145 1         7 $$self{'m'} = [ @default_ztable_m ];
146 1         16 $$self{up} = [ @default_ztable_up ];
147 1         15 $$self{dn} = [ @default_ztable_dn ];
148             # Patch table (and lose DjVu compatibility)
149 1 50       5 unless ($djvucompat) {
150 0         0 my ($p, $m, $dn) = ($$self{p}, $$self{'m'}, $$self{dn});
151 0         0 for ($j=0; $j<256; $j++) {
152 0         0 my $a = (0x10000 - $$p[$j]) & 0xffff;
153 0         0 while ($a >= 0x8000) { $a = ($a<<1) & 0xffff }
  0         0  
154 0 0 0     0 if ($$m[$j]>0 && $a+$$p[$j]>=0x8000 && $a>=$$m[$j]) {
      0        
155 0         0 $$dn[$j] = $default_ztable_dn[$default_ztable_dn[$j]];
156             }
157             }
158             }
159 1         22 $$self{ctx} = [ (0) x 300 ];
160 1         4 $$self{DataPt} = $dataPt;
161 1         4 $$self{Pos} = 0;
162 1         4 $$self{DataLen} = length $$dataPt;
163 1         2 $$self{a} = 0;
164 1         3 $$self{buffer} = 0;
165 1         1 $$self{fence} = 0;
166 1         3 $$self{blocksize} = 0;
167             # Read first 16 bits of code
168 1 50       5 if (length($$dataPt) >= 2) {
    0          
169 1         8 $$self{code} = unpack('n', $$dataPt);
170 1         2 $$self{Pos} += 2;
171             } elsif (length($$dataPt) >= 1) {
172 0         0 $$self{code} = (unpack('C', $$dataPt) << 8) | 0xff;
173 0         0 $$self{Pos}++;
174             } else {
175 0         0 $$self{code} = 0xffff;
176             }
177 1         3 $$self{byte} = $$self{code} & 0xff;
178             # Preload buffer
179 1         4 $$self{delay} = 25;
180 1         2 $$self{scount} = 0;
181             # Compute initial fence
182 1 50       5 $$self{fence} = $$self{code} >= 0x8000 ? 0x7fff : $$self{code};
183             }
184              
185             #------------------------------------------------------------------------------
186             # Decode data block
187             # Inputs: 0) optional BZZ object ref, 1) optional data ref
188             # Returns: decoded data or undefined on error
189             # Notes: If called without a data ref, an input BZZ object ref must be given and
190             # the BZZ object must have been initialized by a previous call to Init()
191             sub Decode($;$)
192             {
193             # Decode input stream
194 1     1 0 2 local $_;
195 1         3 my $self;
196 1 50 33     13 if (ref $_[0] and UNIVERSAL::isa($_[0],'Image::ExifTool::BZZ')) {
197 0         0 $self = shift;
198             } else {
199 1         4 $self = new Image::ExifTool::BZZ;
200             }
201 1         2 my $dataPt = shift;
202 1 50       4 if ($dataPt) {
203 1         3 $self->Init($dataPt, 1);
204             } else {
205 0 0       0 $dataPt = $$self{DataPt} or return undef;
206             }
207             # Decode block size
208 1         3 my $n = 1;
209 1         2 my $m = (1 << 24);
210 1         4 while ($n < $m) {
211 24         39 my $b = $self->decode_sub(0x8000 + ($$self{a}>>1));
212 24         41 $n = ($n<<1) | $b;
213             }
214 1         3 $$self{size} = $n - $m;
215              
216 1 50       3 return '' unless $$self{size};
217 1 50       6 return undef if $$self{size} > MAXBLOCK()*1024;
218             # Allocate
219 1 50       4 if ($$self{blocksize} < $$self{size}) {
220 1         10 $$self{blocksize} = $$self{size};
221             }
222             # Decode Estimation Speed
223 1         3 my $fshift = 0;
224 1 50       3 if ($self->decode_sub(0x8000 + ($$self{a}>>1))) {
225 0         0 $fshift += 1;
226 0 0       0 $fshift += 1 if $self->decode_sub(0x8000 + ($$self{a}>>1));
227             }
228             # Prepare Quasi MTF
229 1         33 my @mtf = (0..255);
230 1         4 my @freq = (0) x FREQMAX();
231 1         3 my $fadd = 4;
232             # Decode
233 1         3 my $mtfno = 3;
234 1         2 my $markerpos = -1;
235 1         2 my $cx = $$self{ctx};
236 1         3 my ($i, @dat);
237 1         5 byte: for ($i=0; $i<$$self{size}; $i++) {
238             # dummy loop avoids use of "goto" statement
239 1966         2113 dummy: for (;;) {
240 1966         2303 my $ctxid = CTXIDS() - 1;
241 1966 100       2921 $ctxid = $mtfno if $ctxid > $mtfno;
242 1966         2001 my $cp = 0;
243 1966         2150 my ($imtf, $bits);
244 1966         2866 for ($imtf=0; $imtf<2; ++$imtf) {
245 2672 100       3770 if ($self->decoder($$cx[$cp+$ctxid])) {
246 1382         1416 $mtfno = $imtf;
247 1382         1967 $dat[$i] = $mtf[$mtfno];
248             # (a "goto" here could give a segfault due to a Perl bug)
249 1382         1773 last dummy; # do rotation
250             }
251 1290         1974 $cp += CTXIDS();
252             }
253 584         941 for ($bits=1; $bits<8; ++$bits, $imtf<<=1) {
254 1969 100       2867 if ($self->decoder($$cx[$cp])) {
255 583         618 my $n = 1;
256 583         670 my $m = (1 << $bits);
257 583         885 while ($n < $m) {
258 1962         2814 my $b = $self->decoder($$cx[$cp+$n]);
259 1962         3305 $n = ($n<<1) | $b;
260             }
261 583         642 $mtfno = $imtf + $n - $m;
262 583         803 $dat[$i] = $mtf[$mtfno];
263 583         877 last dummy; # do rotation
264             }
265 1386         2284 $cp += $imtf;
266             }
267 1         7 $mtfno=256;
268 1         2 $dat[$i] = 0;
269 1         3 $markerpos=$i;
270 1         3 next byte; # no rotation necessary
271             }
272             # Rotate mtf according to empirical frequencies (new!)
273             # Adjust frequencies for overflow
274 1965         2268 $fadd = $fadd + ($fadd >> $fshift);
275 1965 100       3088 if ($fadd > 0x10000000) {
276 81         105 $fadd >>= 24;
277 81         307 $_ >>= 24 foreach @freq;
278             }
279             # Relocate new char according to new freq
280 1965         2101 my $fc = $fadd;
281 1965 100       2459 $fc += $freq[$mtfno] if $mtfno < FREQMAX();
282 1965         2084 my $k;
283 1965         2491 for ($k=$mtfno; $k>=FREQMAX(); $k--) {
284 12981         16988 $mtf[$k] = $mtf[$k-1];
285             }
286 1965   66     3779 for (; $k>0 && $fc>=$freq[$k-1]; $k--) {
287 1822         2078 $mtf[$k] = $mtf[$k-1];
288 1822         3956 $freq[$k] = $freq[$k-1];
289             }
290 1965         2265 $mtf[$k] = $dat[$i];
291 1965         3407 $freq[$k] = $fc;
292             # when "goto" was used, Perl 5.8.6 could segfault here
293             # unless "next" was explicitly stated
294             }
295             #
296             # Reconstruct the string
297             #
298 1 50 33     25 return undef if $markerpos<1 || $markerpos>=$$self{size};
299             # Allocate pointers
300             # Prepare count buffer
301 1         15 my @count = (0) x 256;
302 1         2 my @posn;
303             # Fill count buffer
304 1     1   11 no integer;
  1         3  
  1         6  
305 1         8 for ($i=0; $i<$markerpos; $i++) {
306 266         284 my $c = $dat[$i];
307 266         445 $posn[$i] = ($c<<24) | ($count[$c]++ & 0xffffff);
308             }
309 1         4 $posn[$i++] = 0; # (initialize marker entry just to be safe)
310 1         4 for ( ; $i<$$self{size}; $i++) {
311 1699         1858 my $c = $dat[$i];
312 1699         2815 $posn[$i] = ($c<<24) | ($count[$c]++ & 0xffffff);
313             }
314 1     1   105 use integer;
  1         3  
  1         4  
315             # Compute sorted char positions
316 1         7 my $last = 1;
317 1         8 for ($i=0; $i<256; $i++) {
318 256         271 my $tmp = $count[$i];
319 256         264 $count[$i] = $last;
320 256         348 $last += $tmp;
321             }
322             # Undo the sort transform
323 1         3 $i = 0;
324 1         3 $last = $$self{size}-1;
325 1         5 while ($last > 0) {
326 1965         2357 my $n = $posn[$i];
327 1     1   67 no integer;
  1         3  
  1         9  
328 1965         2116 my $c = $n >> 24;
329 1     1   38 use integer;
  1         7  
  1         5  
330 1965         2189 $dat[--$last] = $c;
331 1965         2875 $i = $count[$c] + ($n & 0xffffff);
332             }
333             # Final check and return decoded data
334 1 50       13 return undef if $i != $markerpos;
335 1         5 pop @dat; # (last byte isn't real)
336 1         243 return pack 'C*', @dat;
337             }
338              
339             #------------------------------------------------------------------------------
340             # Inputs: 0) BZZ object ref, 1) ctx
341             # Returns: decoded bit
342             sub decoder($$)
343             {
344 6603     6603 0 8078 my ($self, $ctx) = @_;
345 6603         8137 my $z = $$self{a} + $self->{p}[$ctx];
346 6603 100       9431 if ($z <= $$self{fence}) {
347 2416         2604 $$self{a} = $z;
348 2416         4028 return ($ctx & 1);
349             }
350             # must pass $_[1] so subroutine can modify value (darned C++ pass-by-reference!)
351 4187         5413 return $self->decode_sub($z, $_[1]);
352             }
353              
354             #------------------------------------------------------------------------------
355             # Inputs: 0) BZZ object ref, 1) z, 2) ctx (or undef)
356             # Returns: decoded bit
357             sub decode_sub($$;$)
358             {
359 4212     4212 0 5252 my ($self, $z, $ctx) = @_;
360              
361             # ensure that we have at least 16 bits of encoded data available
362 4212 100       6253 if ($$self{scount} < 16) {
363             # preload byte by byte until we have at least 24 bits
364 367         623 while ($$self{scount} <= 24) {
365 736 100       1009 if ($$self{Pos} < $$self{DataLen}) {
366 734         740 $$self{byte} = ord(substr(${$$self{DataPt}}, $$self{Pos}, 1));
  734         1308  
367 734         949 ++$$self{Pos};
368             } else {
369 2         8 $$self{byte} = 0xff;
370 2 50       10 if (--$$self{delay} < 1) {
371             # setting size to zero forces error return from Decode()
372 0         0 $$self{size} = 0;
373 0         0 return 0;
374             }
375             }
376 736         921 $$self{buffer} = ($$self{buffer}<<8) | $$self{byte};
377 736         1151 $$self{scount} += 8;
378             }
379             }
380             # Save bit
381 4212         4652 my $a = $$self{a};
382 4212         4526 my ($bit, $code);
383 4212 100       5438 if (defined $ctx) {
384 4187         4673 $bit = ($ctx & 1);
385             # Avoid interval reversion
386 4187         4634 my $d = 0x6000 + (($z+$a)>>2);
387 4187 100       6048 $z = $d if $z > $d;
388             } else {
389 25         26 $bit = 0;
390             }
391             # Test MPS/LPS
392 4212 100       5554 if ($z > ($code = $$self{code})) {
393 2203         2299 $bit ^= 1;
394             # LPS branch
395 2203         2321 $z = 0x10000 - $z;
396 2203         2242 $a += $z;
397 2203         2215 $code += $z;
398             # LPS adaptation
399 2203 100       3296 $_[2] = $self->{dn}[$ctx] if defined $ctx;
400             # LPS renormalization
401 2203 50       3571 my $sft = $a>=0xff00 ? $self->{ffzt}[$a&0xff] + 8 : $self->{ffzt}[($a>>8)&0xff];
402 2203         2496 $$self{scount} -= $sft;
403 2203         2535 $$self{a} = ($a<<$sft) & 0xffff;
404 2203         3005 $code = (($code<<$sft) & 0xffff) | (($$self{buffer}>>$$self{scount}) & ((1<<$sft)-1));
405             } else {
406             # MPS adaptation
407 2009 100 100     5149 $_[2] = $self->{up}[$ctx] if defined $ctx and $a >= $self->{'m'}[$ctx];
408             # MPS renormalization
409 2009         2415 $$self{scount} -= 1;
410 2009         2397 $$self{a} = ($z<<1) & 0xffff;
411 2009         2570 $code = (($code<<1) & 0xffff) | (($$self{buffer}>>$$self{scount}) & 1);
412             }
413             # Adjust fence and save new code
414 4212 100       5755 $$self{fence} = $code >= 0x8000 ? 0x7fff : $code;
415 4212         4697 $$self{code} = $code;
416 4212         6707 return $bit;
417             }
418              
419             1; # end
420              
421             __END__