Files
ipxe/src/crypto/deflate.c
Michael Brown adcaaf9b93 [build] Mark known reviewed files as permitted for UEFI Secure Boot
Some past security reviews carried out for UEFI Secure Boot signing
submissions have covered specific drivers or functional areas of iPXE.
Mark all of the files comprising these areas as permitted for UEFI
Secure Boot.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2026-01-14 16:10:29 +00:00

1043 lines
27 KiB
C

/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <assert.h>
#include <ctype.h>
#include <ipxe/deflate.h>
/** @file
*
* DEFLATE decompression algorithm
*
* This file implements the decompression half of the DEFLATE
* algorithm specified in RFC 1951.
*
* Portions of this code are derived from wimboot's xca.c.
*
*/
/**
* Byte reversal table
*
* For some insane reason, the DEFLATE format stores some values in
* bit-reversed order.
*/
static uint8_t deflate_reverse[256];
/** Literal/length base values
*
* We include entries only for literal/length codes 257-284. Code 285
* does not fit the pattern (it represents a length of 258; following
* the pattern from the earlier codes would give a length of 259), and
* has no extra bits. Codes 286-287 are invalid, but can occur. We
* treat any code greater than 284 as meaning "length 258, no extra
* bits".
*/
static uint8_t deflate_litlen_base[28];
/** Distance base values
*
* We include entries for all possible codes 0-31, avoiding the need
* to check for undefined codes 30 and 31 before performing the
* lookup. Codes 30 and 31 are never initialised, and will therefore
* be treated as meaning "14 extra bits, base distance 0".
*/
static uint16_t deflate_distance_base[32];
/** Code length map */
static uint8_t deflate_codelen_map[19] = {
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
/** Static Huffman alphabet length patterns */
static struct deflate_static_length_pattern deflate_static_length_patterns[] = {
/* Literal/length code lengths */
{ 0x88, ( ( ( 143 - 0 ) + 1 ) / 2 ) },
{ 0x99, ( ( ( 255 - 144 ) + 1 ) / 2 ) },
{ 0x77, ( ( ( 279 - 256 ) + 1 ) / 2 ) },
{ 0x88, ( ( ( 287 - 280 ) + 1 ) / 2 ) },
/* Distance code lengths */
{ 0x55, ( ( ( 31 - 0 ) + 1 ) / 2 ) },
/* End marker */
{ 0, 0 }
};
/**
* Transcribe binary value (for debugging)
*
* @v value Value
* @v bits Length of value (in bits)
* @ret string Transcribed value
*/
static const char * deflate_bin ( unsigned long value, unsigned int bits ) {
static char buf[ ( 8 * sizeof ( value ) ) + 1 /* NUL */ ];
char *out = buf;
/* Sanity check */
assert ( bits < sizeof ( buf ) );
/* Transcribe value */
while ( bits-- )
*(out++) = ( ( value & ( 1 << bits ) ) ? '1' : '0' );
*out = '\0';
return buf;
}
/**
* Set Huffman symbol length
*
* @v deflate Decompressor
* @v index Index within lengths
* @v bits Symbol length (in bits)
*/
static void deflate_set_length ( struct deflate *deflate, unsigned int index,
unsigned int bits ) {
deflate->lengths[ index / 2 ] |= ( bits << ( 4 * ( index % 2 ) ) );
}
/**
* Get Huffman symbol length
*
* @v deflate Decompressor
* @v index Index within lengths
* @ret bits Symbol length (in bits)
*/
static unsigned int deflate_length ( struct deflate *deflate,
unsigned int index ) {
return ( ( deflate->lengths[ index / 2 ] >> ( 4 * ( index % 2 ) ) )
& 0x0f );
}
/**
* Determine Huffman alphabet name (for debugging)
*
* @v deflate Decompressor
* @v alphabet Huffman alphabet
* @ret name Alphabet name
*/
static const char * deflate_alphabet_name ( struct deflate *deflate,
struct deflate_alphabet *alphabet ){
if ( alphabet == &deflate->litlen ) {
return "litlen";
} else if ( alphabet == &deflate->distance_codelen ) {
return "distance/codelen";
} else {
return "<UNKNOWN>";
}
}
/**
* Dump Huffman alphabet (for debugging)
*
* @v deflate Decompressor
* @v alphabet Huffman alphabet
*/
static void deflate_dump_alphabet ( struct deflate *deflate,
struct deflate_alphabet *alphabet ) {
struct deflate_huf_symbols *huf_sym;
unsigned int bits;
unsigned int huf;
unsigned int i;
/* Do nothing unless debugging is enabled */
if ( ! DBG_EXTRA )
return;
/* Dump symbol table for each utilised length */
for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) /
sizeof ( alphabet->huf[0] ) ) ; bits++ ) {
huf_sym = &alphabet->huf[ bits - 1 ];
if ( huf_sym->freq == 0 )
continue;
huf = ( huf_sym->start >> huf_sym->shift );
DBGC2 ( alphabet, "DEFLATE %p \"%s\" length %d start \"%s\" "
"freq %d:", deflate,
deflate_alphabet_name ( deflate, alphabet ), bits,
deflate_bin ( huf, huf_sym->bits ), huf_sym->freq );
for ( i = 0 ; i < huf_sym->freq ; i++ ) {
DBGC2 ( alphabet, " %03x",
huf_sym->raw[ huf + i ] );
}
DBGC2 ( alphabet, "\n" );
}
/* Dump quick lookup table */
DBGC2 ( alphabet, "DEFLATE %p \"%s\" quick lookup:", deflate,
deflate_alphabet_name ( deflate, alphabet ) );
for ( i = 0 ; i < ( sizeof ( alphabet->lookup ) /
sizeof ( alphabet->lookup[0] ) ) ; i++ ) {
DBGC2 ( alphabet, " %d", ( alphabet->lookup[i] + 1 ) );
}
DBGC2 ( alphabet, "\n" );
}
/**
* Construct Huffman alphabet
*
* @v deflate Decompressor
* @v alphabet Huffman alphabet
* @v count Number of symbols
* @v offset Starting offset within length table
* @ret rc Return status code
*/
static int deflate_alphabet ( struct deflate *deflate,
struct deflate_alphabet *alphabet,
unsigned int count, unsigned int offset ) {
struct deflate_huf_symbols *huf_sym;
unsigned int huf;
unsigned int cum_freq;
unsigned int bits;
unsigned int raw;
unsigned int adjustment;
unsigned int prefix;
int complete;
/* Clear symbol table */
memset ( alphabet->huf, 0, sizeof ( alphabet->huf ) );
/* Count number of symbols with each Huffman-coded length */
for ( raw = 0 ; raw < count ; raw++ ) {
bits = deflate_length ( deflate, ( raw + offset ) );
if ( bits )
alphabet->huf[ bits - 1 ].freq++;
}
/* Populate Huffman-coded symbol table */
huf = 0;
cum_freq = 0;
for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) /
sizeof ( alphabet->huf[0] ) ) ; bits++ ) {
huf_sym = &alphabet->huf[ bits - 1 ];
huf_sym->bits = bits;
huf_sym->shift = ( 16 - bits );
huf_sym->start = ( huf << huf_sym->shift );
huf_sym->raw = &alphabet->raw[cum_freq];
huf += huf_sym->freq;
if ( huf > ( 1U << bits ) ) {
DBGC ( alphabet, "DEFLATE %p \"%s\" has too many "
"symbols with lengths <=%d\n", deflate,
deflate_alphabet_name ( deflate, alphabet ),
bits );
return -EINVAL;
}
huf <<= 1;
cum_freq += huf_sym->freq;
}
complete = ( huf == ( 1U << bits ) );
/* Populate raw symbol table */
for ( raw = 0 ; raw < count ; raw++ ) {
bits = deflate_length ( deflate, ( raw + offset ) );
if ( bits ) {
huf_sym = &alphabet->huf[ bits - 1 ];
*(huf_sym->raw++) = raw;
}
}
/* Adjust Huffman-coded symbol table raw pointers and populate
* quick lookup table.
*/
for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) /
sizeof ( alphabet->huf[0] ) ) ; bits++ ) {
huf_sym = &alphabet->huf[ bits - 1 ];
/* Adjust raw pointer */
huf_sym->raw -= huf_sym->freq; /* Reset to first symbol */
adjustment = ( huf_sym->start >> huf_sym->shift );
huf_sym->raw -= adjustment; /* Adjust for quick indexing */
/* Populate quick lookup table */
for ( prefix = ( huf_sym->start >> DEFLATE_HUFFMAN_QL_SHIFT ) ;
prefix < ( 1 << DEFLATE_HUFFMAN_QL_BITS ) ; prefix++ ) {
alphabet->lookup[prefix] = ( bits - 1 );
}
}
/* Dump alphabet (for debugging) */
deflate_dump_alphabet ( deflate, alphabet );
/* Check that there are no invalid codes */
if ( ! complete ) {
DBGC ( alphabet, "DEFLATE %p \"%s\" is incomplete\n", deflate,
deflate_alphabet_name ( deflate, alphabet ) );
return -EINVAL;
}
return 0;
}
/**
* Attempt to accumulate bits from input stream
*
* @v deflate Decompressor
* @v target Number of bits to accumulate
* @ret excess Number of excess bits accumulated (may be negative)
*/
static int deflate_accumulate ( struct deflate *deflate,
unsigned int target ) {
uint8_t byte;
while ( deflate->bits < target ) {
/* Check for end of input */
if ( deflate->in == deflate->end )
break;
/* Acquire byte from input */
byte = *(deflate->in++);
deflate->accumulator = ( deflate->accumulator |
( byte << deflate->bits ) );
deflate->rotalumucca = ( deflate->rotalumucca |
( deflate_reverse[byte] <<
( 24 - deflate->bits ) ) );
deflate->bits += 8;
/* Sanity check */
assert ( deflate->bits <=
( 8 * sizeof ( deflate->accumulator ) ) );
}
return ( deflate->bits - target );
}
/**
* Consume accumulated bits from the input stream
*
* @v deflate Decompressor
* @v count Number of accumulated bits to consume
* @ret data Consumed bits
*/
static int deflate_consume ( struct deflate *deflate, unsigned int count ) {
int data;
/* Sanity check */
assert ( count <= deflate->bits );
/* Extract data and consume bits */
data = ( deflate->accumulator & ( ( 1 << count ) - 1 ) );
deflate->accumulator >>= count;
deflate->rotalumucca <<= count;
deflate->bits -= count;
return data;
}
/**
* Attempt to extract a fixed number of bits from input stream
*
* @v deflate Decompressor
* @v target Number of bits to extract
* @ret data Extracted bits (or negative if not yet accumulated)
*/
static int deflate_extract ( struct deflate *deflate, unsigned int target ) {
int excess;
int data;
/* Return immediately if we are attempting to extract zero bits */
if ( target == 0 )
return 0;
/* Attempt to accumulate bits */
excess = deflate_accumulate ( deflate, target );
if ( excess < 0 )
return excess;
/* Extract data and consume bits */
data = deflate_consume ( deflate, target );
DBGCP ( deflate, "DEFLATE %p extracted %s = %#x = %d\n", deflate,
deflate_bin ( data, target ), data, data );
return data;
}
/**
* Attempt to decode a Huffman-coded symbol from input stream
*
* @v deflate Decompressor
* @v alphabet Huffman alphabet
* @ret code Raw code (or negative if not yet accumulated)
*/
static int deflate_decode ( struct deflate *deflate,
struct deflate_alphabet *alphabet ) {
struct deflate_huf_symbols *huf_sym;
uint16_t huf;
unsigned int lookup_index;
int excess;
unsigned int raw;
/* Attempt to accumulate maximum required number of bits.
* There may be fewer bits than this remaining in the stream,
* even if the stream still contains some complete
* Huffman-coded symbols.
*/
deflate_accumulate ( deflate, DEFLATE_HUFFMAN_BITS );
/* Normalise the bit-reversed accumulated value to 16 bits */
huf = ( deflate->rotalumucca >> 16 );
/* Find symbol set for this length */
lookup_index = ( huf >> DEFLATE_HUFFMAN_QL_SHIFT );
huf_sym = &alphabet->huf[ alphabet->lookup[ lookup_index ] ];
while ( huf < huf_sym->start )
huf_sym--;
/* Calculate number of excess bits, and return if not yet complete */
excess = ( deflate->bits - huf_sym->bits );
if ( excess < 0 )
return excess;
/* Consume bits */
deflate_consume ( deflate, huf_sym->bits );
/* Look up raw symbol */
raw = huf_sym->raw[ huf >> huf_sym->shift ];
DBGCP ( deflate, "DEFLATE %p decoded %s = %#x = %d\n", deflate,
deflate_bin ( ( huf >> huf_sym->shift ), huf_sym->bits ),
raw, raw );
return raw;
}
/**
* Discard bits up to the next byte boundary
*
* @v deflate Decompressor
*/
static void deflate_discard_to_byte ( struct deflate *deflate ) {
deflate_consume ( deflate, ( deflate->bits & 7 ) );
}
/**
* Copy data to output buffer (if available)
*
* @v out Output data buffer
* @v in Input data
* @v len Length to copy
*/
static void deflate_copy ( struct deflate_chunk *out, const void *in,
size_t len ) {
const uint8_t *in_byte = in;
uint8_t *out_byte = ( out->data + out->offset );
size_t copy_len;
/* Copy data one byte at a time, to allow for overlap */
if ( out->offset < out->len ) {
copy_len = ( out->len - out->offset );
if ( copy_len > len )
copy_len = len;
while ( copy_len-- )
*(out_byte++) = *(in_byte++);
}
out->offset += len;
}
/**
* Inflate compressed data
*
* @v deflate Decompressor
* @v data Compressed input data
* @v len Length of compressed input data
* @v out Output data buffer
* @ret rc Return status code
*
* The caller can use deflate_finished() to determine whether a
* successful return indicates that the decompressor is merely waiting
* for more input.
*
* Data will not be written beyond the specified end of the output
* data buffer, but the offset within the output data buffer will be
* updated to reflect the amount that should have been written. The
* caller can use this to find the length of the decompressed data
* before allocating the output data buffer.
*/
int deflate_inflate ( struct deflate *deflate, const void *data, size_t len,
struct deflate_chunk *out ) {
/* Store input data pointers */
deflate->in = data;
deflate->end = ( data + len );
/* This could be implemented more neatly if gcc offered a
* means for enforcing tail recursion.
*/
if ( deflate->resume ) {
goto *(deflate->resume);
} else switch ( deflate->format ) {
case DEFLATE_RAW: goto block_header;
case DEFLATE_ZLIB: goto zlib_header;
default: assert ( 0 );
}
zlib_header: {
int header;
int cm;
/* Extract header */
header = deflate_extract ( deflate, ZLIB_HEADER_BITS );
if ( header < 0 ) {
deflate->resume = &&zlib_header;
return 0;
}
/* Parse header */
cm = ( ( header >> ZLIB_HEADER_CM_LSB ) & ZLIB_HEADER_CM_MASK );
if ( cm != ZLIB_HEADER_CM_DEFLATE ) {
DBGC ( deflate, "DEFLATE %p unsupported ZLIB "
"compression method %d\n", deflate, cm );
return -ENOTSUP;
}
if ( header & ( 1 << ZLIB_HEADER_FDICT_BIT ) ) {
DBGC ( deflate, "DEFLATE %p unsupported ZLIB preset "
"dictionary\n", deflate );
return -ENOTSUP;
}
/* Process first block header */
goto block_header;
}
block_header: {
int header;
int bfinal;
int btype;
/* Extract block header */
header = deflate_extract ( deflate, DEFLATE_HEADER_BITS );
if ( header < 0 ) {
deflate->resume = &&block_header;
return 0;
}
/* Parse header */
deflate->header = header;
bfinal = ( header & ( 1 << DEFLATE_HEADER_BFINAL_BIT ) );
btype = ( header >> DEFLATE_HEADER_BTYPE_LSB );
DBGC ( deflate, "DEFLATE %p found %sblock type %#x\n",
deflate, ( bfinal ? "final " : "" ), btype );
switch ( btype ) {
case DEFLATE_HEADER_BTYPE_LITERAL:
goto literal_block;
case DEFLATE_HEADER_BTYPE_STATIC:
goto static_block;
case DEFLATE_HEADER_BTYPE_DYNAMIC:
goto dynamic_block;
default:
DBGC ( deflate, "DEFLATE %p unsupported block type "
"%#x\n", deflate, btype );
return -ENOTSUP;
}
}
literal_block: {
/* Discard any bits up to the next byte boundary */
deflate_discard_to_byte ( deflate );
}
literal_len: {
int llen;
/* Extract LEN field */
llen = deflate_extract ( deflate, DEFLATE_LITERAL_LEN_BITS );
if ( llen < 0 ) {
deflate->resume = &&literal_len;
return 0;
}
/* Record length of literal data */
deflate->remaining = llen;
DBGC2 ( deflate, "DEFLATE %p literal block length %#04zx\n",
deflate, deflate->remaining );
}
literal_nlen: {
int nlen;
/* Extract NLEN field */
nlen = deflate_extract ( deflate, DEFLATE_LITERAL_LEN_BITS );
if ( nlen < 0 ) {
deflate->resume = &&literal_nlen;
return 0;
}
/* Verify NLEN */
if ( ( ( deflate->remaining ^ ~nlen ) &
( ( 1 << DEFLATE_LITERAL_LEN_BITS ) - 1 ) ) != 0 ) {
DBGC ( deflate, "DEFLATE %p invalid len/nlen "
"%#04zx/%#04x\n", deflate,
deflate->remaining, nlen );
return -EINVAL;
}
}
literal_data: {
size_t in_remaining;
size_t dlen;
/* Calculate available amount of literal data */
in_remaining = ( deflate->end - deflate->in );
dlen = deflate->remaining;
if ( dlen > in_remaining )
dlen = in_remaining;
/* Copy data to output buffer */
deflate_copy ( out, deflate->in, dlen );
/* Consume data from input buffer */
deflate->in += dlen;
deflate->remaining -= dlen;
/* Finish processing if we are blocked */
if ( deflate->remaining ) {
deflate->resume = &&literal_data;
return 0;
}
/* Otherwise, finish block */
goto block_done;
}
static_block: {
struct deflate_static_length_pattern *pattern;
uint8_t *lengths = deflate->lengths;
/* Construct static Huffman lengths as per RFC 1950 */
for ( pattern = deflate_static_length_patterns ;
pattern->count ; pattern++ ) {
memset ( lengths, pattern->fill, pattern->count );
lengths += pattern->count;
}
deflate->litlen_count = 288;
deflate->distance_count = 32;
goto construct_alphabets;
}
dynamic_block:
dynamic_header: {
int header;
unsigned int hlit;
unsigned int hdist;
unsigned int hclen;
/* Extract block header */
header = deflate_extract ( deflate, DEFLATE_DYNAMIC_BITS );
if ( header < 0 ) {
deflate->resume = &&dynamic_header;
return 0;
}
/* Parse header */
hlit = ( ( header >> DEFLATE_DYNAMIC_HLIT_LSB ) &
DEFLATE_DYNAMIC_HLIT_MASK );
hdist = ( ( header >> DEFLATE_DYNAMIC_HDIST_LSB ) &
DEFLATE_DYNAMIC_HDIST_MASK );
hclen = ( ( header >> DEFLATE_DYNAMIC_HCLEN_LSB ) &
DEFLATE_DYNAMIC_HCLEN_MASK );
deflate->litlen_count = ( hlit + 257 );
deflate->distance_count = ( hdist + 1 );
deflate->length_index = 0;
deflate->length_target = ( hclen + 4 );
DBGC2 ( deflate, "DEFLATE %p dynamic block %d codelen, %d "
"litlen, %d distance\n", deflate,
deflate->length_target, deflate->litlen_count,
deflate->distance_count );
/* Prepare for decoding code length code lengths */
memset ( &deflate->lengths, 0, sizeof ( deflate->lengths ) );
}
dynamic_codelen: {
int clen;
unsigned int index;
int rc;
/* Extract all code lengths */
while ( deflate->length_index < deflate->length_target ) {
/* Extract code length length */
clen = deflate_extract ( deflate,
DEFLATE_CODELEN_BITS );
if ( clen < 0 ) {
deflate->resume = &&dynamic_codelen;
return 0;
}
/* Store code length */
index = deflate_codelen_map[deflate->length_index++];
deflate_set_length ( deflate, index, clen );
DBGCP ( deflate, "DEFLATE %p codelen for %d is %d\n",
deflate, index, clen );
}
/* Generate code length alphabet */
if ( ( rc = deflate_alphabet ( deflate,
&deflate->distance_codelen,
( DEFLATE_CODELEN_MAX_CODE + 1 ),
0 ) ) != 0 )
return rc;
/* Prepare for decoding literal/length/distance code lengths */
memset ( &deflate->lengths, 0, sizeof ( deflate->lengths ) );
deflate->length_index = 0;
deflate->length_target = ( deflate->litlen_count +
deflate->distance_count );
deflate->length = 0;
}
dynamic_litlen_distance: {
int clen;
int index;
/* Decode literal/length/distance code length */
clen = deflate_decode ( deflate, &deflate->distance_codelen );
if ( clen < 0 ) {
deflate->resume = &&dynamic_litlen_distance;
return 0;
}
/* Prepare for extra bits */
if ( clen < 16 ) {
deflate->length = clen;
deflate->extra_bits = 0;
deflate->dup_len = 1;
} else {
static const uint8_t dup_len[3] = { 3, 3, 11 };
static const uint8_t extra_bits[3] = { 2, 3, 7 };
index = ( clen - 16 );
deflate->dup_len = dup_len[index];
deflate->extra_bits = extra_bits[index];
if ( index )
deflate->length = 0;
}
}
dynamic_litlen_distance_extra: {
int extra;
unsigned int dup_len;
/* Extract extra bits */
extra = deflate_extract ( deflate, deflate->extra_bits );
if ( extra < 0 ) {
deflate->resume = &&dynamic_litlen_distance_extra;
return 0;
}
/* Store code lengths */
dup_len = ( deflate->dup_len + extra );
while ( ( deflate->length_index < deflate->length_target ) &&
dup_len-- ) {
deflate_set_length ( deflate, deflate->length_index++,
deflate->length );
}
/* Process next literal/length or distance code
* length, if more are required.
*/
if ( deflate->length_index < deflate->length_target )
goto dynamic_litlen_distance;
/* Construct alphabets */
goto construct_alphabets;
}
construct_alphabets: {
unsigned int distance_offset = deflate->litlen_count;
unsigned int distance_count = deflate->distance_count;
int rc;
/* Generate literal/length alphabet */
if ( ( rc = deflate_alphabet ( deflate, &deflate->litlen,
deflate->litlen_count, 0 ) ) !=0)
return rc;
/* Handle degenerate case of a single distance code
* (for which it is impossible to construct a valid,
* complete Huffman alphabet). RFC 1951 states:
*
* If only one distance code is used, it is encoded
* using one bit, not zero bits; in this case there
* is a single code length of one, with one unused
* code. One distance code of zero bits means that
* there are no distance codes used at all (the data
* is all literals).
*
* If we have only a single distance code, then we
* instead use two distance codes both with length 1.
* This results in a valid Huffman alphabet. The code
* "0" will mean distance code 0 (which is either
* correct or irrelevant), and the code "1" will mean
* distance code 1 (which is always irrelevant).
*/
if ( deflate->distance_count == 1 ) {
deflate->lengths[0] = 0x11;
distance_offset = 0;
distance_count = 2;
}
/* Generate distance alphabet */
if ( ( rc = deflate_alphabet ( deflate,
&deflate->distance_codelen,
distance_count,
distance_offset ) ) != 0 )
return rc;
}
lzhuf_litlen: {
int code;
uint8_t byte;
unsigned int extra;
unsigned int bits;
/* Decode Huffman codes */
while ( 1 ) {
/* Decode Huffman code */
code = deflate_decode ( deflate, &deflate->litlen );
if ( code < 0 ) {
deflate->resume = &&lzhuf_litlen;
return 0;
}
/* Handle according to code type */
if ( code < DEFLATE_LITLEN_END ) {
/* Literal value: copy to output buffer */
byte = code;
DBGCP ( deflate, "DEFLATE %p literal %#02x "
"('%c')\n", deflate, byte,
( isprint ( byte ) ? byte : '.' ) );
deflate_copy ( out, &byte, sizeof ( byte ) );
} else if ( code == DEFLATE_LITLEN_END ) {
/* End of block */
goto block_done;
} else {
/* Length code: process extra bits */
extra = ( code - DEFLATE_LITLEN_END - 1 );
if ( extra < 28 ) {
bits = ( extra / 4 );
if ( bits )
bits--;
deflate->extra_bits = bits;
deflate->dup_len =
deflate_litlen_base[extra];
} else {
deflate->extra_bits = 0;
deflate->dup_len = 258;
}
goto lzhuf_litlen_extra;
}
}
}
lzhuf_litlen_extra: {
int extra;
/* Extract extra bits */
extra = deflate_extract ( deflate, deflate->extra_bits );
if ( extra < 0 ) {
deflate->resume = &&lzhuf_litlen_extra;
return 0;
}
/* Update duplicate length */
deflate->dup_len += extra;
}
lzhuf_distance: {
int code;
unsigned int extra;
unsigned int bits;
/* Decode Huffman code */
code = deflate_decode ( deflate, &deflate->distance_codelen );
if ( code < 0 ) {
deflate->resume = &&lzhuf_distance;
return 0;
}
/* Process extra bits */
extra = code;
bits = ( extra / 2 );
if ( bits )
bits--;
deflate->extra_bits = bits;
deflate->dup_distance = deflate_distance_base[extra];
}
lzhuf_distance_extra: {
int extra;
size_t dup_len;
size_t dup_distance;
/* Extract extra bits */
extra = deflate_extract ( deflate, deflate->extra_bits );
if ( extra < 0 ) {
deflate->resume = &&lzhuf_distance_extra;
return 0;
}
/* Update duplicate distance */
dup_distance = ( deflate->dup_distance + extra );
dup_len = deflate->dup_len;
DBGCP ( deflate, "DEFLATE %p duplicate length %zd distance "
"%zd\n", deflate, dup_len, dup_distance );
/* Sanity check */
if ( dup_distance > out->offset ) {
DBGC ( deflate, "DEFLATE %p bad distance %zd (max "
"%zd)\n", deflate, dup_distance, out->offset );
return -EINVAL;
}
/* Copy data, allowing for overlap */
deflate_copy ( out, ( out->data + out->offset - dup_distance ),
dup_len );
/* Process next literal/length symbol */
goto lzhuf_litlen;
}
block_done: {
DBGCP ( deflate, "DEFLATE %p end of block\n", deflate );
/* If this was not the final block, process next block header */
if ( ! ( deflate->header & ( 1 << DEFLATE_HEADER_BFINAL_BIT ) ))
goto block_header;
/* Otherwise, process footer (if any) */
switch ( deflate->format ) {
case DEFLATE_RAW: goto finished;
case DEFLATE_ZLIB: goto zlib_footer;
default: assert ( 0 );
}
}
zlib_footer: {
/* Discard any bits up to the next byte boundary */
deflate_discard_to_byte ( deflate );
}
zlib_adler32: {
int excess;
/* Accumulate the 32 bits of checksum. We don't check
* the value, stop processing immediately afterwards,
* and so don't have to worry about the nasty corner
* cases involved in calling deflate_extract() to
* obtain a full 32 bits.
*/
excess = deflate_accumulate ( deflate, ZLIB_ADLER32_BITS );
if ( excess < 0 ) {
deflate->resume = &&zlib_adler32;
return 0;
}
/* Finish processing */
goto finished;
}
finished: {
/* Mark as finished and terminate */
DBGCP ( deflate, "DEFLATE %p finished\n", deflate );
deflate->resume = NULL;
return 0;
}
}
/**
* Initialise decompressor
*
* @v deflate Decompressor
* @v format Compression format code
*/
void deflate_init ( struct deflate *deflate, enum deflate_format format ) {
static int global_init_done;
uint8_t i;
uint8_t bit;
uint8_t byte;
unsigned int base;
unsigned int bits;
/* Perform global initialisation if required */
if ( ! global_init_done ) {
/* Initialise byte reversal table */
for ( i = 255 ; i ; i-- ) {
for ( bit = 1, byte = 0 ; bit ; bit <<= 1 ) {
byte <<= 1;
if ( i & bit )
byte |= 1;
}
deflate_reverse[i] = byte;
}
/* Initialise literal/length extra bits table */
base = 3;
for ( i = 0 ; i < 28 ; i++ ) {
bits = ( i / 4 );
if ( bits )
bits--;
deflate_litlen_base[i] = base;
base += ( 1 << bits );
}
assert ( base == 259 ); /* sic */
/* Initialise distance extra bits table */
base = 1;
for ( i = 0 ; i < 30 ; i++ ) {
bits = ( i / 2 );
if ( bits )
bits--;
deflate_distance_base[i] = base;
base += ( 1 << bits );
}
assert ( base == 32769 );
/* Record global initialisation as complete */
global_init_done = 1;
}
/* Initialise structure */
memset ( deflate, 0, sizeof ( *deflate ) );
deflate->format = format;
}