blob: 07ecce70f554551fff7196f204de42ce2a6779e8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2011 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - Initial API and implementation
*******************************************************************************/
#include "compression.h"
#include "huffman_byte_encoder.h"
enum Errors compression_error_no = NO_ERROR;
/* prints diagnostic error message for last error. */
void print_compression_error(void)
{
fprintf(stderr, "%s\n", error_strings[compression_error_no]);
}
/* free the memory associated with this buffer as well as this buffer */
void free_compression_buffer(struct compression_buffer *buf)
{
free(buf->data);
free(buf);
}
/* allocate compression buffer. Returns a compression buffer with memory mem_size or NULL in case of failure */
struct compression_buffer * alloc_compression_buffer(int mem_size)
{
struct compression_buffer *ret = (struct compression_buffer *)malloc(sizeof(struct compression_buffer));
if (ret == 0)
return NULL;
ret->data = (unsigned char *) malloc (mem_size);
if (ret->data == NULL)
{
free(ret);
return NULL;
}
return ret;
}
/* reallocs the compression buffer buf to new size new_mem_size. Returns the newly allocated buffer or NULL in case of failure */
void *realloc_compression_buffer(struct compression_buffer *buf, int new_mem_size)
{
return buf->data = realloc(buf->data, new_mem_size);
}
/* sets the pos bit in pattern to 1 if val is non-zero, otherwise to 0.*/
void set_bit(unsigned char *pattern, int pos, int val)
{
pattern += pos / NBITSPERBYTE;
val?((*pattern) |= 1 << pos % NBITSPERBYTE):((*pattern) &= ~(1 << pos % NBITSPERBYTE));
}
/* return non-zero if this machine is big endian architecture, otherwise zero */
int is_bigendian()
{
unsigned int i = 1;
return !(*(unsigned char *)&i);
}
/* reverses the byte order of int representation */
void swap_int(unsigned int *in)
{
unsigned int result = 0;
unsigned char *c1 = (unsigned char *)in;
unsigned char *c2 = (unsigned char *)&result;
c2[0] = c1[3]; c2[1] = c1[2];
c2[2] = c1[1]; c2[3] = c1[0];
*in = result;
}
/* initializes the compression.
method - the algorithm (currently only HUFFMAN)
comp_object - the compression object to initialize
returns 1 on success and 0 on failure.
*/
int init_compression(int method, struct compression_method *comp_object)
{
compression_error_no = NO_ERROR;
if (comp_object == NULL)
{
compression_error_no = INVALID_ARGUMENT;
return 0;
}
comp_object->decode_info = NULL;
comp_object->compression_table = NULL;
comp_object->free_list = NULL;
switch(method)
{
case BYTE_HUFFMAN:
{
comp_object->type = BYTE_HUFFMAN;
break;
}
default:
compression_error_no = UNKNOWN_COMPRESSION_METHOD;
return 0;
}
comp_object->encoder = &huffman_encoder;
comp_object->decoder = &huffman_decoder;
comp_object->get_dictionary = &huffman_decode_info;
comp_object->initializer = &init_byte_huffman;
comp_object->destructor = &free_huffman;
comp_object->get_symbol = &huffman_get_symbol;
comp_object->get_symbol_rep = &huffman_get_symbol_rep;
comp_object->parser = NULL; /* constant 1 byte symbols */
if (comp_object->initializer(comp_object) == 0)
return 0;
return 1;
}
/* cleanup and free any resources used by algorithm
method - the compression method object
*/
void end_compression(struct compression_method *method)
{
compression_error_no = NO_ERROR;
method->destructor(method);
}
/* convention - lsb is 1st bit for below functions. Should use the same in Java */
/* generic function to pack bits of any len
pack len buts from pattern to stream starting at bit position at
*/
void pack_bits_into_stream(unsigned char *stream, unsigned char *pattern, int at, int len)
{
int offset = at % NBITSPERBYTE;
unsigned char *dest = stream + at / NBITSPERBYTE;
int mask;
while(len > 0)
{
/* extract lower (NBITSPERBYTE - offset) bits from pattern */
mask = (1 << (NBITSPERBYTE - offset)) - 1;
*dest &= ~(mask << offset);
*dest |= ((*pattern) & mask) << offset;
*++dest = 0;
*dest |= (*pattern) >> (NBITSPERBYTE - offset);
pattern++;
len -= NBITSPERBYTE;
}
}
/* special case of pack_bits_into_stream - bits to be packed at current byte in stream at offset and len <= 8 bits */
static void pack_byte_bits(unsigned char *dest, unsigned char *pattern, int offset, int len)
{
if (offset)
{
if (NBITSPERBYTE - offset >= len)
{
*dest &= ~(((1 << (NBITSPERBYTE - offset)) - 1) << offset);
*dest |= (*pattern) << offset;
}
else
{
int mask = ((1 << (NBITSPERBYTE - offset)) - 1);
*dest &= ~(mask << offset);
*dest |= (*pattern) << offset;
*(dest + 1) = (*pattern & ~mask) >> (NBITSPERBYTE - offset);
}
}
else
*dest = *pattern;
}
/* compresses the input buffer and returns the compressed buffer
method - the compression method object
in_buf - the input buffer to compress. Should have uncompressed_len set to number of bytes in buffer
returns - the compressed buffer with compressed_len set to number of bytes in this buffer and uncompressed_len set to original number of bytes. Returns NULL in case of failure.
*/
struct compression_buffer *compress(struct compression_method *method, struct compression_buffer *in_buf)
{
struct compression_buffer *out;
int current_buf_len;
int bytes_left;
unsigned char *curr_ptr;
int bit_position = NBITSPERBYTE * sizeof(int);
int decode_len = 0;
int symbol_len;
int encoding_len;
struct symbol *s;
compression_error_no = NO_ERROR;
unsigned char *decode_info = NULL;
if (method == NULL || in_buf == NULL || in_buf->uncompressed_len == 0)
{
compression_error_no = INVALID_ARGUMENT;
return NULL;
}
current_buf_len = (in_buf->uncompressed_len + sizeof(int) ) * NBITSPERBYTE;
bytes_left = in_buf->uncompressed_len;
curr_ptr = in_buf->data;
get_symbol_func get_symbol = method->get_symbol;
if (method->decode_info == NULL)
{
if (method->encoder(method, in_buf) == 0)
return NULL;
if ((decode_info = method->get_dictionary(method, &decode_len)) == NULL)
return NULL;
current_buf_len += decode_len * NBITSPERBYTE;
bit_position += NBITSPERBYTE * decode_len;
}
if ((out = alloc_compression_buffer(current_buf_len / NBITSPERBYTE + 1 )) == NULL)
{
compression_error_no = OUT_OF_MEMORY;
return 0;
}
stream_parser_func parser = method->parser;
while(bytes_left)
{
symbol_len = (parser == NULL)?1:parser(curr_ptr);
s = get_symbol(method, curr_ptr, symbol_len);
encoding_len = s->encoding_length;
/* one might end up with a larger output */
if ((encoding_len + bit_position) > current_buf_len )
{
current_buf_len += NBITSPERBYTE * encoding_len + current_buf_len;
if (realloc_compression_buffer(out, current_buf_len/NBITSPERBYTE + 1 ) == NULL)
{
compression_error_no = OUT_OF_MEMORY;
return 0;
}
}
pack_bits_into_stream(out->data, s->encoding, bit_position, encoding_len);
bit_position += encoding_len;
curr_ptr += symbol_len;
bytes_left-=symbol_len;
}
out->compressed_len = bit_position?(1 + bit_position / NBITSPERBYTE):0;
*((int *)(out->data )) = out->uncompressed_len = in_buf->uncompressed_len;
if (!is_bigendian())
swap_int( (unsigned int *)(out->data) );
if (decode_len)
memcpy(out->data + sizeof(int), decode_info, decode_len);
return out;
}
/* uncompresses the input buffer and returns the uncompressed buffer
method - the compression method object
in_buf - the input buffer to uncompress with compressed_len set to the size of this buffer and uncompressed_len set to the original uncompressed len.
returns - the uncompressed buffer with uncompressed_len set to the uncompressed len in bytes. Returns NULL in case of failure.
*/
struct compression_buffer *uncompress(struct compression_method *method, struct compression_buffer *in_buf)
{
int uncompressed_len;
compression_error_no = NO_ERROR;
struct compression_buffer *ret;
if (in_buf == NULL || method == NULL)
{
compression_error_no = INVALID_ARGUMENT;
return NULL;
}
decoder_type decoder = method->decoder;
/* unpack header data */
uncompressed_len = *((int *)(in_buf->data));
if (!is_bigendian())
swap_int((unsigned int *)&uncompressed_len);
ret = decoder(method, in_buf->data + sizeof(int), in_buf->compressed_len, uncompressed_len, in_buf->flags & COMPRESSION_TABLE_FLAG);
return ret;
}