| 95 | | } |
| 96 | | |
| 97 | | sub decode { # modified from slide.c:decode |
| 98 | | my $self = shift; |
| 99 | | |
| 100 | | my $queue = Archive::Lha::Queue->new( |
| 101 | | size => $self->DICSIZE, |
| 102 | | callback => $self->{write}, |
| 103 | | ); |
| 104 | | my $adjust = ( 1 << UCHAR_BIT ) - $self->THRESHOLD; |
| 105 | | my $total = 0; |
| 106 | | my $original = $self->{original}; |
| 107 | | |
| 108 | | while ( $total < $original ) { |
| 109 | | my $char = $self->_decode_c; |
| 110 | | if ( $char <= UCHAR_MAX ) { |
| 111 | | $queue->set( $char ); |
| 112 | | $total++; |
| 113 | | } |
| 114 | | else { |
| 115 | | my $length = $char - $adjust; |
| 116 | | my $offset = $self->_decode_p + 1; |
| 117 | | |
| 118 | | $queue->copy( $offset, $length ); |
| 119 | | $total += $length; |
| 120 | | } |
| 121 | | } |
| 122 | | $queue->output; |
| 123 | | croak "CRC mismatch" if $queue->crc16 != $self->{crc16}; |
| 124 | | } |
| 125 | | |
| 126 | | sub _decode_c { # modified from huf.c:decode_c |
| 127 | | my $self = shift; |
| 128 | | |
| 129 | | if ( $self->{blocksize} == 0 ) { |
| 130 | | $self->{blocksize} = $self->bit->read( USHORT_BIT ); |
| 131 | | $self->_read_pt( $self->NT, $self->TBIT, 3 ); |
| 132 | | $self->_read_c; |
| 133 | | $self->_read_pt( $self->NP, $self->PBIT, -1 ); |
| 134 | | } |
| 135 | | $self->{blocksize}--; |
| 136 | | |
| 137 | | my $char = $self->c->value( $self->bit->peek( $self->c->bit ) ); |
| 138 | | |
| 139 | | if ( $char < $self->c->length_size ) { |
| 140 | | $self->bit->cut( $self->c->length( $char ) ); |
| 141 | | } |
| 142 | | else { |
| 143 | | $self->bit->cut( $self->c->bit ); |
| 144 | | $char = $self->tree->_follow( |
| 145 | | from => $char, |
| 146 | | bits => $self->bit->peek( USHORT_BIT ), |
| 147 | | limit => $self->c->length_size, |
| 148 | | ); |
| 149 | | $self->bit->cut( $self->c->length( $char ) - $self->c->bit ); |
| 150 | | } |
| 151 | | return $char; |
| 152 | | } |
| 153 | | |
| 154 | | sub _decode_p { # modified from huf.c:decode_p |
| 155 | | my $self = shift; |
| 156 | | |
| 157 | | my $pt = $self->pt->value( $self->bit->peek( $self->pt->bit ) ); |
| 158 | | |
| 159 | | if ( $pt < $self->NP ) { |
| 160 | | $self->bit->cut( $self->pt->length( $pt ) ); |
| 161 | | } |
| 162 | | else { |
| 163 | | $self->bit->cut( $self->pt->bit ); |
| 164 | | $pt = $self->tree->_follow( |
| 165 | | from => $pt, |
| 166 | | bits => $self->bit->peek( USHORT_BIT ), |
| 167 | | limit => $self->NP, |
| 168 | | ); |
| 169 | | $self->bit->cut( $self->pt->length( $pt ) - $self->pt->bit ); |
| 170 | | } |
| 171 | | if ( $pt ) { |
| 172 | | $pt = ( 1 << ( $pt - 1 ) ) + $self->bit->read( $pt - 1 ); |
| 173 | | } |
| 174 | | return $pt; |
| 175 | | } |
| 176 | | |
| 177 | | sub _read_pt { # modified from huf.c:read_pt_len |
| 178 | | my ($self, $nn, $nbit, $threshold) = @_; |
| 179 | | |
| 180 | | my $n = $self->bit->read( $nbit ); |
| 181 | | if ( $n == 0 ) { |
| 182 | | $self->pt->fill_value( with => $self->bit->read( $nbit ) ); |
| 183 | | $self->pt->fill_length( with => 0, to => $nn ); |
| 184 | | } |
| 185 | | else { |
| 186 | | my $i = 0; |
| 187 | | while ( $i < min( $n, $self->pt->length_size ) ) { |
| 188 | | my $c = $self->bit->peek(3); |
| 189 | | if ( $c == 7 ) { |
| 190 | | my $mask = _ushort( 1 << ( ( USHORT_BIT - 1 ) - 3 ) ); |
| 191 | | while ( $self->bit->check( $mask ) ) { |
| 192 | | $mask >>= 1; |
| 193 | | $c++; |
| 194 | | } |
| 195 | | $self->bit->cut( $c - 3 ); |
| 196 | | } |
| 197 | | else { |
| 198 | | $self->bit->cut(3); |
| 199 | | } |
| 200 | | |
| 201 | | $self->pt->length( $i++ => $c ); |
| 202 | | if ( $i == $threshold ) { |
| 203 | | $c = $self->bit->read(2); |
| 204 | | while( --$c >= 0 && $i < $self->pt->length_size ) { |
| 205 | | $self->pt->length( $i++ => 0 ); |
| 206 | | } |
| 207 | | } |
| 208 | | } |
| 209 | | $self->pt->fill_length( with => 0, from => $i, to => $nn ); |
| 210 | | $self->_make_table( $self->pt, $nn ); |
| 211 | | } |
| 212 | | } |
| 213 | | |
| 214 | | sub _read_c { # modified from huf.c:read_c_len |
| 215 | | my $self = shift; |
| 216 | | |
| 217 | | my $n = $self->bit->read( $self->CBIT ); |
| 218 | | if ( $n == 0 ) { |
| 219 | | $self->c->fill_value( with => $self->bit->read( $self->CBIT ) ); |
| 220 | | $self->c->fill_length( with => 0 ); |
| 221 | | } |
| 222 | | else { |
| 223 | | my $i = 0; |
| 224 | | while ( $i < min( $n, $self->c->length_size ) ) { |
| 225 | | my $c = $self->pt->value( $self->bit->peek( $self->pt->bit ) ); |
| 226 | | if ( $c >= $self->NT ) { |
| 227 | | $c = $self->tree->_follow( |
| 228 | | from => $c, |
| 229 | | bits => $self->bit->peek( USHORT_BIT ), |
| 230 | | mask_shift => $self->pt->bit, |
| 231 | | limit => $self->NT, |
| 232 | | ); |
| 233 | | } |
| 234 | | $self->bit->cut( $self->pt->length( $c ) ); |
| 235 | | if ( $c <= 2 ) { |
| 236 | | if ( $c == 0 ) { |
| 237 | | $c = 1; |
| 238 | | } |
| 239 | | elsif ( $c == 1 ) { |
| 240 | | $c = $self->bit->read(4) + 3; |
| 241 | | } |
| 242 | | else { |
| 243 | | $c = $self->bit->read( $self->CBIT ) + 20; |
| 244 | | } |
| 245 | | while ( --$c >= 0 ) { |
| 246 | | $self->c->length( $i++ => 0 ); |
| 247 | | } |
| 248 | | } |
| 249 | | else { |
| 250 | | $self->c->length( $i++ => $c - 2 ); |
| 251 | | } |
| 252 | | } |
| 253 | | $self->c->fill_length( with => 0, from => $i ); |
| 254 | | $self->_make_table( $self->c, $self->c->length_size ); |
| 255 | | } |
| 256 | | } |
| 257 | | |
| 258 | | sub _make_table { # modified from maketbl.c:make_table |
| 259 | | my ($self, $table, $nchar) = @_; |
| 260 | | |
| 261 | | my (@count, @weight, @start); |
| 262 | | |
| 263 | | for( my $i = 1; $i <= USHORT_BIT; $i++ ) { |
| 264 | | $count[$i] = 0; |
| 265 | | $weight[$i] = _ushort( 1 << ( USHORT_BIT - $i ) ); |
| 266 | | } |
| 267 | | |
| 268 | | for( my $i = 0; $i < $nchar; $i++ ) { |
| 269 | | if ( $table->length( $i ) > USHORT_BIT ) { |
| 270 | | croak "Table is broken: ".$table->stringify; |
| 271 | | } |
| 272 | | $count[ $table->length( $i ) ]++; |
| 273 | | } |
| 274 | | |
| 275 | | my $total = 0; |
| 276 | | for( my $i = 1; $i <= USHORT_BIT; $i++ ) { |
| 277 | | $start[$i] = $total; |
| 278 | | $total += $weight[$i] * $count[$i]; |
| 279 | | } |
| 280 | | |
| 281 | | if ( $total & USHORT_MAX ) { |
| 282 | | croak "Table is broken: ".$table->stringify; |
| 283 | | } |
| 284 | | |
| 285 | | my $m = USHORT_BIT - $table->bit; |
| 286 | | |
| 287 | | for( my $i = 1; $i <= $table->bit; $i++ ) { |
| 288 | | $start[$i] = _ushort( $start[$i] >> $m ); |
| 289 | | $weight[$i] = _ushort( $weight[$i] >> $m ); |
| 290 | | } |
| 291 | | |
| 292 | | my $j = $start[ $table->bit + 1 ] >> $m; |
| 293 | | my $k = min( 1 << $table->bit, $table->size ); |
| 294 | | |
| 295 | | if ( $j ) { |
| 296 | | $table->fill_value( with => 0, from => $j, to => $k ); |
| 297 | | } |
| 298 | | |
| 299 | | my $avail = $nchar; |
| 300 | | for( my $j = 0; $j < $nchar; $j++ ) { |
| 301 | | my $k = $table->length( $j ) or next; |
| 302 | | my $l = $start[$k] + $weight[$k]; |
| 303 | | |
| 304 | | if ( $k <= $table->bit ) { |
| 305 | | $l = min( $l, $table->size ); |
| 306 | | $table->fill_value( with => $j, from => $start[$k], to => $l ); |
| 307 | | } |
| 308 | | else { |
| 309 | | my $i = $start[$k]; |
| 310 | | if ( ( $i >> $m ) > $table->size ) { |
| 311 | | croak "Table is broken: ".$table->stringify; |
| 312 | | } |
| 313 | | |
| 314 | | my $p_addr = $i >> $m; |
| 315 | | my $p_type = 'table'; |
| 316 | | my $p = $table->value( $p_addr ); |
| 317 | | my $n = $k - $table->bit; |
| 318 | | $i = _ushort( $i << $table->bit ); |
| 319 | | |
| 320 | | while ( --$n >= 0 ) { |
| 321 | | if ( $p == 0 ) { |
| 322 | | $self->tree->right( $avail => 0 ); |
| 323 | | $self->tree->left( $avail => 0 ); |
| 324 | | $p = $avail++; |
| 325 | | if ( $p_type eq 'table' ) { |
| 326 | | $table->value( $p_addr => $p ); |
| 327 | | } |
| 328 | | elsif ( $p_type eq 'right' ) { |
| 329 | | $self->tree->right( $p_addr => $p ); |
| 330 | | } |
| 331 | | elsif ( $p_type eq 'left' ) { |
| 332 | | $self->tree->left( $p_addr => $p ); |
| 333 | | } |
| 334 | | } |
| 335 | | if ( $i >= USHORT_CENTER ) { |
| 336 | | $p_type = 'right'; |
| 337 | | $p_addr = $p; |
| 338 | | $p = $self->tree->right( $p_addr ); |
| 339 | | } |
| 340 | | else { |
| 341 | | $p_type = 'left'; |
| 342 | | $p_addr = $p; |
| 343 | | $p = $self->tree->left( $p_addr ); |
| 344 | | } |
| 345 | | $i = _ushort( $i << 1 ); |
| 346 | | } |
| 347 | | if ( $p_type eq 'table' ) { |
| 348 | | $table->value( $p_addr => $j ); |
| 349 | | } |
| 350 | | elsif ( $p_type eq 'right' ) { |
| 351 | | $self->tree->right( $p_addr => $j ); |
| 352 | | } |
| 353 | | elsif ( $p_type eq 'left' ) { |
| 354 | | $self->tree->left( $p_addr => $j ); |
| 355 | | } |
| 356 | | } |
| 357 | | $start[$k] = $l; |
| 358 | | } |