diff options
Diffstat (limited to 'cpukit/libdl/rtl-obj-comp.c')
-rw-r--r-- | cpukit/libdl/rtl-obj-comp.c | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/cpukit/libdl/rtl-obj-comp.c b/cpukit/libdl/rtl-obj-comp.c new file mode 100644 index 0000000000..d9c5462e1e --- /dev/null +++ b/cpukit/libdl/rtl-obj-comp.c @@ -0,0 +1,179 @@ +/* + * COPYRIGHT (c) 2012 Chris Johns <chrisj@rtems.org> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.com/license/LICENSE. + */ +/** + * @file + * + * @ingroup rtems_rtl + * + * @brief RTEMS Run-Time Linker Object Compression manages a compress + * stream of data. + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include <rtems/rtl/rtl-allocator.h> +#include "rtl-obj-comp.h" +#include "rtl-error.h" + +#include "fastlz.h" + +#include <stdio.h> + +bool +rtems_rtl_obj_comp_open (rtems_rtl_obj_comp_t* comp, + size_t size) +{ + comp->cache = NULL; + comp->fd = -1; + comp->compression = RTEMS_RTL_COMP_LZ77; + comp->offset = 0; + comp->size = size; + comp->level = 0; + comp->buffer = rtems_rtl_alloc_new (RTEMS_RTL_ALLOC_OBJECT, size, false); + if (!comp->buffer) + { + rtems_rtl_set_error (ENOMEM, "no memory for compressor buffer"); + return false; + } + comp->read = 0; + return true; +} + +void +rtems_rtl_obj_comp_close (rtems_rtl_obj_comp_t* comp) +{ + rtems_rtl_alloc_del (RTEMS_RTL_ALLOC_OBJECT, comp->buffer); + comp->cache = NULL; + comp->fd = -1; + comp->compression = RTEMS_RTL_COMP_LZ77; + comp->level = 0; + comp->size = 0; + comp->offset = 0; + comp->read = 0; +} + +void +rtems_rtl_obj_comp_set (rtems_rtl_obj_comp_t* comp, + rtems_rtl_obj_cache_t* cache, + int fd, + int compression, + off_t offset) +{ + comp->cache = cache; + comp->fd = fd; + comp->compression = compression; + comp->offset = offset; + comp->level = 0; + comp->read = 0; +} + +bool +rtems_rtl_obj_comp_read (rtems_rtl_obj_comp_t* comp, + void* buffer, + size_t length) +{ + uint8_t* bin = buffer; + + if (!comp->cache) + { + rtems_rtl_set_error (EINVAL, "not open"); + return false; + } + + if (comp->fd != comp->cache->fd) + { + comp->level = 0; + } + + while (length) + { + size_t buffer_level; + + buffer_level = length > comp->level ? comp->level : length; + + if (buffer_level) + { + memcpy (bin, comp->buffer, buffer_level); + + if ((comp->level - buffer_level) != 0) + { + memmove (comp->buffer, + comp->buffer + buffer_level, + comp->level - buffer_level); + } + + bin += buffer_level; + length -= buffer_level; + comp->level -= buffer_level; + comp->read += buffer_level; + } + + if (length) + { + uint8_t* input = NULL; + uint16_t block_size; + size_t in_length = sizeof (block_size); + int decompressed; + + if (!rtems_rtl_obj_cache_read (comp->cache, comp->fd, comp->offset, + (void**) &input, &in_length)) + return false; + + block_size = (input[0] << 8) | input[1]; + + comp->offset += sizeof (block_size); + + in_length = block_size; + + if (!rtems_rtl_obj_cache_read (comp->cache, comp->fd, comp->offset, + (void**) &input, &in_length)) + return false; + + if (in_length != block_size) + { + rtems_rtl_set_error (EIO, "compressed read failed: bs=%u in=%u", + block_size, in_length); + return false; + } + + switch (comp->compression) + { + case RTEMS_RTL_COMP_NONE: + memcpy (comp->buffer, input, in_length); + decompressed = in_length; + break; + + case RTEMS_RTL_COMP_LZ77: + decompressed = fastlz_decompress (input, in_length, + comp->buffer, comp->size); + if (decompressed == 0) + { + rtems_rtl_set_error (EBADF, "decompression failed"); + return false; + } + break; + + default: + rtems_rtl_set_error (EINVAL, "bad compression type"); + return false; + } + + comp->offset += block_size; + + comp->level = decompressed; + } + } + + return true; +} |