| /******************************************************************************* |
| * 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; |
| } |