summaryrefslogblamecommitdiffstats
path: root/cpukit/libdl/rtl-obj-comp.c
blob: 70f14d351898f7381d801d266ba139fe7ead9c72 (plain) (tree)
1
2
3
4
5
6




                                                           
                                         









































































































































                                                                           
                                                                         

































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