/*
* 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.org/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=%zu",
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;
}