view release on metacpan or search on metacpan
```perl
my $symbols = ac_decode($bits_fh, \%freq);
my $symbols = ac_decode($bitstring, \%freq);
```
Performs Arithmetic Coding decoding using the provided frequency table and a string of 1s and 0s. Inverse of `ac_encode()`.
It takes two parameters: `$bitstring`, representing a string of 1s and 0s containing the arithmetic coded data, and `\%freq`, representing the frequency table used for encoding.
The function returns the decoded sequence of symbols.
## adaptive\_ac\_encode
```perl
my ($bitstring, $alphabet) = adaptive_ac_encode(\@symbols);
```
Performs Adaptive Arithmetic Coding on the provided symbols.
It takes a single parameter, `\@symbols`, representing the symbols to be encoded.
```perl
my $symbols = adaptive_ac_decode($bits_fh, \@alphabet);
my $symbols = adaptive_ac_decode($bitstring, \@alphabet);
```
Performs Adaptive Arithmetic Coding decoding using the provided frequency table and a string of 1s and 0s.
It takes two parameters: `$bitstring`, representing a string of 1s and 0s containing the adaptive arithmetic coded data, and `\@alphabet`, representing the array of distinct sorted symbols that appear in the encoded data.
The function returns the decoded sequence of symbols.
## lzw\_encode
```perl
my $symbols = lzw_encode($string);
```
Performs Lempel-Ziv-Welch (LZW) encoding on the provided string.
It takes a single parameter, `$string`, representing the data to be encoded.
The function returns an array-ref of symbols.
## lzw\_decode
```perl
my $string = lzw_decode(\@symbols);
```
Performs Lempel-Ziv-Welch (LZW) decoding on the provided symbols. Inverse of `lzw_encode()`.
The function returns the decoded string.
# INTERFACE FOR LOW-LEVEL FUNCTIONS
## crc32
```perl
my $int32 = crc32($data);
my $int32 = crc32($data, $prev_crc32);
```
```
Given a string of 1s and 0s, returns back a bitstring of 1s and 0s encoded using variable run-length encoding.
## binary\_vrl\_decode
```perl
my $bitstring = binary_vrl_decode($bitstring_enc);
```
Given an encoded bitstring, returned by `binary_vrl_encode()`, gives back the decoded string of 1s and 0s.
## bwt\_sort
```perl
my $indices = bwt_sort($string);
my $indices = bwt_sort($string, $lookahead_len);
```
Low-level function that sorts the rotations of a given string using the Burrows-Wheeler Transform (BWT) algorithm.
## huffman\_decode
```perl
my $symbols = huffman_decode($bitstring, $rev_dict);
```
Low-level function that decodes a Huffman-encoded binary string into a sequence of symbols using a provided reverse dictionary.
It takes two parameters: `$bitstring`, representing the Huffman-encoded string of 1s and 0s, as returned by `huffman_encode()`, and `$rev_dict`, representing the reverse dictionary mapping Huffman codes to their corresponding symbols.
The function returns the decoded sequence of symbols as an array-ref.
## lz77\_encode / lz77\_encode\_symbolic
```perl
my ($literals, $distances, $lengths, $matches) = lz77_encode($string);
my ($literals, $distances, $lengths, $matches) = lz77_encode(\@symbols);
```
Low-level function that combines LZSS with ideas from the LZ4 method.
The function returns four values:
```perl
$literals # array-ref of uncompressed symbols
$distances # array-ref of back-reference distances
$lengths # array-ref of literal lengths
$matches # array-ref of match lengths
```
The output can be decoded with `lz77_decode()` and `lz77_decode_symbolic()`, respectively.
## lz77\_decode / lz77\_decode\_symbolic
```perl
my $string = lz77_decode(\@literals, \@distances, \@lengths, \@matches);
my $symbols = lz77_decode_symbolic(\@literals, \@distances, \@lengths, \@matches);
```
Low-level function that performs decoding using the provided literals, distances, lengths and matches, returned by LZ77 encoding.
```
The function returns three values:
```perl
$literals # array-ref of uncompressed symbols
$distances # array-ref of back-reference distances
$lengths # array-ref of match lengths
```
The output can be decoded with `lzss_decode()` and `lzss_decode_symbolic()`, respectively.
## lzss\_decode / lzss\_decode\_symbolic
my $string = lzss_decode(\@literals, \@distances, \@lengths);
my $symbols = lzss_decode_symbolic(\@literals, \@distances, \@lengths);
Low-level function that decodes the LZSS encoding, using the provided literals, distances, and lengths of matched sub-strings.
Inverse of `lzss_encode()` and `lzss_encode_fast()`.
```perl
my $data = deflate_extract_block_type_0($fh, \$buffer, \$search_window);
```
Given an input filehandle, it extracts a DEFLATE block of type 0 (uncompressed).
```perl
my ($buffer, $search_window) = ('', '');
my $block_type = bits2int_lsb($fh, 2, \$buffer);
$block_type == 0 or die "Not a block of type 0";
my $decoded_chunk = deflate_extract_block_type_0($fh, \$buffer, \$search_window);
```
## deflate\_extract\_block\_type\_1
```perl
my $data = deflate_extract_block_type_1($fh, \$buffer, \$search_window);
```
Given an input filehandle, a bitstring buffer and a search window, it extracts a DEFLATE block of type 1 (fixed prefix-codes).
```perl
my ($buffer, $search_window) = ('', '');
my $block_type = bits2int_lsb($fh, 2, \$buffer);
$block_type == 1 or die "Not a block of type 1";
my $decoded_chunk = deflate_extract_block_type_1($fh, \$buffer, \$search_window);
```
## deflate\_extract\_block\_type\_2
```perl
my $data = deflate_extract_block_type_2($fh, \$buffer, \$search_window);
```
Given an input filehandle, a bitstring buffer and a search window, it extracts a DEFLATE block of type 2 (dynamic prefix-codes).
```perl
my ($buffer, $search_window) = ('', '');
my $block_type = bits2int_lsb($fh, 2, \$buffer);
$block_type == 2 or die "Not a block of type 2";
my $decoded_chunk = deflate_extract_block_type_2($fh, \$buffer, \$search_window);
```
## deflate\_extract\_next\_block
```perl
my $data = deflate_extract_next_block($fh, \$buffer, \$search_window);
```
Given an input filehandle, a bitstring buffer and a search window, it extracts the next DEFLATE block. The next two bits in the input file-handle (or in the bitstring buffer) must contain the block-type number.
examples/bzip2_decompressor.pl view on Meta::CPAN
my @huffman_trees = map { (huffman_from_code_lengths($_))[1] } @trees;
my $eob = @alphabet + 1;
my @zrle;
my $code = '';
my $sel_idx = 0;
my $tree = $huffman_trees[$sels->[$sel_idx]];
my $decoded = 50;
while (!eof($fh)) {
$code .= read_bit($fh, \$buffer);
if (length($code) > $MaxHuffmanBits) {
die "[!] Something went wrong: length of LL code `$code` is > $MaxHuffmanBits.\n";
}
if (exists($tree->{$code})) {
my $sym = $tree->{$code};
if ($sym == $eob) { # end of block marker
say STDERR "EOB detected: $sym";
last;
}
push @zrle, $sym;
$code = '';
if (--$decoded <= 0) {
if (++$sel_idx <= $#$sels) {
$tree = $huffman_trees[$sels->[$sel_idx]];
}
else {
die "No more selectors"; # should not happen
}
$decoded = 50;
}
}
}
##say STDERR "ZRLE: (@zrle)";
my @mtf = reverse @{zrle_decode([reverse @zrle])};
##say STDERR "MTF: (@mtf)";
my $bwt = symbols2string mtf_decode(\@mtf, \@alphabet);
## say "BWT: ($bwt, $bwt_idx)";
lib/Compression/Util.pm view on Meta::CPAN
my $t = sprintf('%b', $v - 1);
$encoded .= join('', '1' x length($t), '0', substr($t, 1));
}
}
return $encoded;
}
sub binary_vrl_decode ($bitstring) {
my $decoded = '';
my $bit = substr($bitstring, 0, 1, '');
while ($bitstring ne '') {
$decoded .= $bit;
my $bl = 0;
while (substr($bitstring, 0, 1, '') eq '1') {
++$bl;
}
if ($bl > 0) {
$decoded .= $bit x oct('0b1' . join('', map { substr($bitstring, 0, 1, '') } 1 .. $bl - 1));
}
$bit = ($bit eq '1' ? '0' : '1');
}
return $decoded;
}
############################
# Burrows-Wheeler transform
############################
sub bwt_sort ($s, $LOOKAHEAD_LEN = 128) { # O(n * LOOKAHEAD_LEN) space (fast)
#<<<
[
map { $_->[1] } sort {
lib/Compression/Util.pm view on Meta::CPAN
$k += $run;
push @deltas, ($deltas[-1]) x $run;
}
}
if ($k == 0) {
$len = pop(@deltas);
}
}
my $decoded = \@deltas;
$decoded = rle4_decode($decoded) if $with_rle4;
$decoded = accumulate($decoded) if $with_deltas;
return $decoded;
}
################################
# Alphabet encoding (from Bzip2)
################################
sub encode_alphabet_256 ($alphabet) {
my %table;
@table{@$alphabet} = ();
lib/Compression/Util.pm view on Meta::CPAN
my @huffman_trees = map { (huffman_from_code_lengths($_))[1] } @trees;
my $eob = @alphabet + 1;
my @zrle;
my $code = '';
my $sel_idx = 0;
my $tree = $huffman_trees[$sels->[$sel_idx]];
my $decoded = 50;
while (!eof($fh)) {
$code .= read_bit($fh, \$buffer);
if (length($code) > $MaxHuffmanBits) {
confess "[!] Something went wrong: length of code `$code` is > $MaxHuffmanBits.";
}
if (exists($tree->{$code})) {
my $sym = $tree->{$code};
if ($sym == $eob) { # end of block marker
$VERBOSE && say STDERR "EOB detected: $sym";
last;
}
push @zrle, $sym;
$code = '';
if (--$decoded <= 0) {
if (++$sel_idx <= $#$sels) {
$tree = $huffman_trees[$sels->[$sel_idx]];
}
else {
confess "No more selectors"; # should not happen
}
$decoded = 50;
}
}
}
my @mtf = reverse @{zrle_decode([reverse @zrle])};
my $bwt = symbols2string mtf_decode(\@mtf, \@alphabet);
my $rle4 = string2symbols bwt_decode($bwt, $bwt_idx);
my $data = rle4_decode($rle4);
my $dec = symbols2string($data);
lib/Compression/Util.pm view on Meta::CPAN
if ($DictID) {
my $dict_id = bytes2int_lsb($fh, 4);
$VERBOSE && say STDERR "Dictionary ID: ", $dict_id;
}
my $header_checksum = ord(getc($fh));
# TODO: compute and verify the header checksum
$VERBOSE && say STDERR "Header checksum: ", $header_checksum;
my $decoded = '';
while (!eof($fh)) {
my $block_size = bytes2int_lsb($fh, 4);
if ($block_size == 0x00000000) { # signifies an EndMark
$VERBOSE && say STDERR "Block size == 0";
last;
}
$VERBOSE && say STDERR "Block size: $block_size";
if ($block_size >> 31) {
$VERBOSE && say STDERR "Highest bit set: ", $block_size;
$block_size &= ((1 << 31) - 1);
$VERBOSE && say STDERR "Block size: ", $block_size;
my $uncompressed = '';
read($fh, $uncompressed, $block_size);
$decoded .= $uncompressed;
}
else {
my $compressed = '';
read($fh, $compressed, $block_size);
while ($compressed ne '') {
my $len_byte = ord(substr($compressed, 0, 1, ''));
my $literals_length = $len_byte >> 4;
lib/Compression/Util.pm view on Meta::CPAN
## say STDERR "Total literals length: ", $literals_length;
my $literals = '';
if ($literals_length > 0) {
$literals = substr($compressed, 0, $literals_length, '');
}
if ($compressed eq '') { # end of block
$decoded .= $literals;
last;
}
my $offset = oct('0b' . reverse unpack('b16', substr($compressed, 0, 2, '')));
if ($offset == 0) {
confess "Corrupted block";
}
## say STDERR "Offset: $offset";
if ($match_len == 15) {
while (1) {
my $byte_len = ord(substr($compressed, 0, 1, ''));
$match_len += $byte_len;
last if $byte_len != 255;
}
}
$decoded .= $literals;
$match_len += 4;
## say STDERR "Total match len: $match_len\n";
if ($offset >= $match_len) { # non-overlapping matches
$decoded .= substr($decoded, length($decoded) - $offset, $match_len);
}
elsif ($offset == 1) {
$decoded .= substr($decoded, -1) x $match_len;
}
else { # overlapping matches
foreach my $i (1 .. $match_len) {
$decoded .= substr($decoded, length($decoded) - $offset, 1);
}
}
}
}
if ($B_checksum) {
my $content_checksum = bytes2int_lsb($fh, 4);
$VERBOSE && say STDERR "Block checksum: $content_checksum";
}
if ($B_indep) { # blocks are independent of each other
$decompressed .= $decoded;
$decoded = '';
}
elsif (length($decoded) > 2**16) { # blocks are dependent
$decompressed .= substr($decoded, 0, -(2**16), '');
}
}
# TODO: compute and verify checksum
if ($C_checksum) {
my $content_checksum = bytes2int_lsb($fh, 4);
$VERBOSE && say STDERR "Content checksum: $content_checksum";
}
$decompressed .= $decoded;
}
return $decompressed;
}
1;
__END__
=encoding utf-8
lib/Compression/Util.pm view on Meta::CPAN
=head2 ac_decode
my $symbols = ac_decode($bits_fh, \%freq);
my $symbols = ac_decode($bitstring, \%freq);
Performs Arithmetic Coding decoding using the provided frequency table and a string of 1s and 0s. Inverse of C<ac_encode()>.
It takes two parameters: C<$bitstring>, representing a string of 1s and 0s containing the arithmetic coded data, and C<\%freq>, representing the frequency table used for encoding.
The function returns the decoded sequence of symbols.
=head2 adaptive_ac_encode
my ($bitstring, $alphabet) = adaptive_ac_encode(\@symbols);
Performs Adaptive Arithmetic Coding on the provided symbols.
It takes a single parameter, C<\@symbols>, representing the symbols to be encoded.
The function returns two values: C<$bitstring>, which is a string of 1s and 0s, and C<$alphabet>, which is an array-ref of distinct sorted symbols.
=head2 adaptive_ac_decode
my $symbols = adaptive_ac_decode($bits_fh, \@alphabet);
my $symbols = adaptive_ac_decode($bitstring, \@alphabet);
Performs Adaptive Arithmetic Coding decoding using the provided frequency table and a string of 1s and 0s.
It takes two parameters: C<$bitstring>, representing a string of 1s and 0s containing the adaptive arithmetic coded data, and C<\@alphabet>, representing the array of distinct sorted symbols that appear in the encoded data.
The function returns the decoded sequence of symbols.
=head2 lzw_encode
my $symbols = lzw_encode($string);
Performs Lempel-Ziv-Welch (LZW) encoding on the provided string.
It takes a single parameter, C<$string>, representing the data to be encoded.
The function returns an array-ref of symbols.
=head2 lzw_decode
my $string = lzw_decode(\@symbols);
Performs Lempel-Ziv-Welch (LZW) decoding on the provided symbols. Inverse of C<lzw_encode()>.
The function returns the decoded string.
=head1 INTERFACE FOR LOW-LEVEL FUNCTIONS
=head2 crc32
my $int32 = crc32($data);
my $int32 = crc32($data, $prev_crc32);
Compute the CRC32 checksum of a given string.
lib/Compression/Util.pm view on Meta::CPAN
=head2 binary_vrl_encode
my $bitstring_enc = binary_vrl_encode($bitstring);
Given a string of 1s and 0s, returns back a bitstring of 1s and 0s encoded using variable run-length encoding.
=head2 binary_vrl_decode
my $bitstring = binary_vrl_decode($bitstring_enc);
Given an encoded bitstring, returned by C<binary_vrl_encode()>, gives back the decoded string of 1s and 0s.
=head2 bwt_sort
my $indices = bwt_sort($string);
my $indices = bwt_sort($string, $lookahead_len);
Low-level function that sorts the rotations of a given string using the Burrows-Wheeler Transform (BWT) algorithm.
It takes two parameters: C<$string>, which is the input string to be transformed, and C<$LOOKAHEAD_LEN> (optional), representing the length of look-ahead during sorting.
lib/Compression/Util.pm view on Meta::CPAN
The function returns a concatenated string of 1s and 0s, representing the Huffman-encoded sequence of symbols.
=head2 huffman_decode
my $symbols = huffman_decode($bitstring, $rev_dict);
Low-level function that decodes a Huffman-encoded binary string into a sequence of symbols using a provided reverse dictionary.
It takes two parameters: C<$bitstring>, representing the Huffman-encoded string of 1s and 0s, as returned by C<huffman_encode()>, and C<$rev_dict>, representing the reverse dictionary mapping Huffman codes to their corresponding symbols.
The function returns the decoded sequence of symbols as an array-ref.
=head2 lz77_encode / lz77_encode_symbolic
my ($literals, $distances, $lengths, $matches) = lz77_encode($string);
my ($literals, $distances, $lengths, $matches) = lz77_encode(\@symbols);
Low-level function that combines LZSS with ideas from the LZ4 method.
The function returns four values:
$literals # array-ref of uncompressed symbols
$distances # array-ref of back-reference distances
$lengths # array-ref of literal lengths
$matches # array-ref of match lengths
The output can be decoded with C<lz77_decode()> and C<lz77_decode_symbolic()>, respectively.
=head2 lz77_decode / lz77_decode_symbolic
my $string = lz77_decode(\@literals, \@distances, \@lengths, \@matches);
my $symbols = lz77_decode_symbolic(\@literals, \@distances, \@lengths, \@matches);
Low-level function that performs decoding using the provided literals, distances, lengths and matches, returned by LZ77 encoding.
Inverse of C<lz77_encode()> and C<lz77_encode_symbolic()>, respectively.
lib/Compression/Util.pm view on Meta::CPAN
max_len => $LZ_MAX_LEN,
max_dist => $LZ_MAX_DIST,
max_chain_len => $LZ_MAX_CHAIN_LEN,
The function returns three values:
$literals # array-ref of uncompressed symbols
$distances # array-ref of back-reference distances
$lengths # array-ref of match lengths
The output can be decoded with C<lzss_decode()> and C<lzss_decode_symbolic()>, respectively.
=head2 lzss_decode / lzss_decode_symbolic
my $string = lzss_decode(\@literals, \@distances, \@lengths);
my $symbols = lzss_decode_symbolic(\@literals, \@distances, \@lengths);
Low-level function that decodes the LZSS encoding, using the provided literals, distances, and lengths of matched sub-strings.
Inverse of C<lzss_encode()> and C<lzss_encode_fast()>.
lib/Compression/Util.pm view on Meta::CPAN
=head2 deflate_extract_block_type_0
my $data = deflate_extract_block_type_0($fh, \$buffer, \$search_window);
Given an input filehandle, it extracts a DEFLATE block of type 0 (uncompressed).
my ($buffer, $search_window) = ('', '');
my $block_type = bits2int_lsb($fh, 2, \$buffer);
$block_type == 0 or die "Not a block of type 0";
my $decoded_chunk = deflate_extract_block_type_0($fh, \$buffer, \$search_window);
=head2 deflate_extract_block_type_1
my $data = deflate_extract_block_type_1($fh, \$buffer, \$search_window);
Given an input filehandle, a bitstring buffer and a search window, it extracts a DEFLATE block of type 1 (fixed prefix-codes).
my ($buffer, $search_window) = ('', '');
my $block_type = bits2int_lsb($fh, 2, \$buffer);
$block_type == 1 or die "Not a block of type 1";
my $decoded_chunk = deflate_extract_block_type_1($fh, \$buffer, \$search_window);
=head2 deflate_extract_block_type_2
my $data = deflate_extract_block_type_2($fh, \$buffer, \$search_window);
Given an input filehandle, a bitstring buffer and a search window, it extracts a DEFLATE block of type 2 (dynamic prefix-codes).
my ($buffer, $search_window) = ('', '');
my $block_type = bits2int_lsb($fh, 2, \$buffer);
$block_type == 2 or die "Not a block of type 2";
my $decoded_chunk = deflate_extract_block_type_2($fh, \$buffer, \$search_window);
=head2 deflate_extract_next_block
my $data = deflate_extract_next_block($fh, \$buffer, \$search_window);
Given an input filehandle, a bitstring buffer and a search window, it extracts the next DEFLATE block. The next two bits in the input file-handle (or in the bitstring buffer) must contain the block-type number.
=head1 EXPORT
Each function can be exported individually, as:
is_deeply(\@symbols, \@copy); # make sure the array has not been modified in-place
}
##################################
{
my $bitstring = "101000010000000010000000100000000001001100010000000000000010010100000000000000001";
my $encoded = binary_vrl_encode($bitstring);
my $decoded = binary_vrl_decode($encoded);
is($decoded, $bitstring);
is($encoded, "1000110101110110111010011110001010101100011110101010000111101110");
}
##############################################
{
my $str = "INEFICIENCIES";
{
my $encoded = mtf_encode(string2symbols($str), [ord('A') .. ord('Z')]);
my $decoded = mtf_decode($encoded, [ord('A') .. ord('Z')]);
is(join(' ', @$encoded), '8 13 6 7 3 6 1 3 4 3 3 3 18');
is($str, symbols2string($decoded));
}
{
my ($encoded, $alphabet) = mtf_encode(string2symbols($str));
my $decoded = mtf_decode($encoded, $alphabet);
is(join(' ', @$encoded), '3 4 3 4 3 4 1 3 4 3 3 3 5');
is($str, symbols2string($decoded));
}
}
##############################################
{
my $int1 = int(rand(1 << 5));
my $int2 = int(rand(1 << 6));
my $int3 = int(rand(1e6));
# DEFLATE block type 0
{
my $chunk = "foobar hello world";
my $bt0_header = deflate_create_block_type_0_header($chunk);
my $block_type_0 = pack('b*', '00') . pack('b*', $bt0_header) . $chunk;
open my $fh, '<:raw', \$block_type_0;
my ($buffer, $search_window) = ('', '');
my $decoded_chunk = deflate_extract_next_block($fh, \$buffer, \$search_window);
is($chunk, $decoded_chunk);
is($search_window, $chunk);
}
# DEFLATE block type 0
{
my $chunk = "hello world 12";
my $bt0_header = deflate_create_block_type_0_header($chunk);
my $block_type_0 = pack('b*', $bt0_header) . $chunk;
my ($buffer, $search_window) = ('', '');
open my $fh, '<:raw', \$block_type_0;
my $decoded_chunk = deflate_extract_block_type_0($fh, \$buffer, \$search_window);
is($chunk, $decoded_chunk);
}
# DEFLATE block type 1
{
my $chunk = "foobar hello world";
my ($literals, $distances, $lengths) = lzss_encode($chunk, min_len => 4, max_len => 258, max_dist => 2**15 - 1);
my $bitstring = deflate_create_block_type_1($literals, $distances, $lengths);
my $block_type_1 = pack('b*', $bitstring);
open my $fh, '<:raw', \$block_type_1;
my ($buffer, $search_window) = ('', '');
my $decoded_chunk = deflate_extract_next_block($fh, \$buffer, \$search_window);
is($chunk, $decoded_chunk);
is($search_window, $chunk);
}
# DEFLATE block type 1
{
my $chunk = "foobar hello world";
my ($literals, $distances, $lengths) = lzss_encode($chunk, min_len => 4, max_len => 258, max_dist => 2**15 - 1);
my $bitstring = deflate_create_block_type_1($literals, $distances, $lengths);
my $block_type_1 = pack('b*', $bitstring);
open my $fh, '<:raw', \$block_type_1;
my ($buffer, $search_window) = ('', '');
my $block_type = bits2int_lsb($fh, 2, \$buffer);
is($block_type, 1);
my $decoded_chunk = deflate_extract_block_type_1($fh, \$buffer, \$search_window);
is($chunk, $decoded_chunk);
is($search_window, $chunk);
}
# DEFLATE block type 2
{
my $chunk = "foobar hello world";
my ($literals, $distances, $lengths) = lzss_encode($chunk, min_len => 4, max_len => 258, max_dist => 2**15 - 1);
my $bitstring = deflate_create_block_type_2($literals, $distances, $lengths);
my $block_type_2 = pack('b*', $bitstring);
open my $fh, '<:raw', \$block_type_2;
my ($buffer, $search_window) = ('', '');
my $decoded_chunk = deflate_extract_next_block($fh, \$buffer, \$search_window);
is($chunk, $decoded_chunk);
is($search_window, $chunk);
}
# DEFLATE block type 2
{
my $chunk = "foobar hello world";
my ($literals, $distances, $lengths) = lzss_encode($chunk, min_len => 4, max_len => 258, max_dist => 2**15 - 1);
my $bitstring = deflate_create_block_type_2($literals, $distances, $lengths);
my $block_type_2 = pack('b*', $bitstring);
open my $fh, '<:raw', \$block_type_2;
my ($buffer, $search_window) = ('', '');
my $block_type = bits2int_lsb($fh, 2, \$buffer);
is($block_type, 2);
my $decoded_chunk = deflate_extract_block_type_2($fh, \$buffer, \$search_window);
is($decoded_chunk, $chunk);
is($search_window, $chunk);
}