packages feed

lz4-bytes-0.2.0.0: cbits/hs_lz4.c

#include "Rts.h"
#include "lz4.h"
#include "lz4frame.h"
#include "lz4hc.h"
#include <string.h>

HsInt hs_compress_fast
  ( const char* src
  , HsInt soff
  , char* dst
  , HsInt doff
  , HsInt slen
  , HsInt dstCapacity
  , HsInt acceleration
  ) {
  HsInt r;
  r = (HsInt)(LZ4_compress_fast(src + soff,dst + doff,slen,dstCapacity,(int)acceleration));
  return r;
}

HsInt hs_compress_HC
  ( const char* src
  , HsInt soff
  , char* dst
  , HsInt doff
  , HsInt slen
  , HsInt dstCapacity
  , HsInt level
  ) {
  HsInt r;
  r = (HsInt)(LZ4_compress_HC(src + soff,dst + doff,slen,dstCapacity,(int)level));
  return r;
}

HsInt hs_decompress_safe
  ( const char* src
  , HsInt soff
  , char* dst
  , HsInt doff
  , HsInt slen
  , HsInt dstCapacity
  ) {
    HsInt r;
    r = (HsInt)LZ4_decompress_safe(src + soff, dst + doff, slen, dstCapacity);
    return r;
  }

// This implementation was copied adapted from the Python lz4.frame subpackage.
// This returns a zero on success. Here are the main changes made to the Python
// code:
// * This function is only called in contexts where the size of the decompressed
//   data is communicated some other way, so we never need to grow the destination.
// * full_frame is removed because it is always true
// * This uses the unsafe FFI, so we can set stableDst to true unconditionally
// * We get rid of return_bytearray and return_bytes_read because we
//   only return an indicator of whether or not the exact number of
//   expected bytes were decompressed.
HsInt hs_decompress_frame(LZ4F_dctx * context, const char* const source, HsInt const source_off, HsInt const source_size, char* const destination, HsInt const decompressed_length)
{
  size_t source_remain = source_size;
  size_t source_read;
  const char * source_cursor;
  const char * source_end;
  char * destination_cursor;
  size_t destination_written;
  size_t result = 0;
  LZ4F_frameInfo_t frame_info;
  LZ4F_decompressOptions_t options = {0};
  int end_of_frame = 0;
  size_t destination_write = decompressed_length;

  options.stableDst = 1;
  options.skipChecksums = 1;

  source_cursor = source + source_off;
  source_end = source + source_off + source_size;
  source_remain = source_size;
  source_read = source_size;

  result = LZ4F_getFrameInfo (context, &frame_info, source_cursor, &source_read);
  if (LZ4F_isError (result)) { return result; }

  /* Advance the source_cursor pointer past the header - the call to
     getFrameInfo above replaces the passed source_read value with the
     number of bytes read. Also reduce source_remain accordingly. */
  source_cursor += source_read;
  source_remain -= source_read;

  /* If the uncompressed content size is available, we'll use that to size
     the destination buffer. Otherwise, guess at twice the remaining source
     source as a starting point, and adjust if needed. */
  if (frame_info.contentSize > 0)
  {
    if (frame_info.contentSize != decompressed_length) {
      return 20000;
    }
  }

  source_read = source_remain;

  destination_cursor = destination;
  destination_written = 0;

  while (1)
    {
      /* Decompress from the source string and write to the destination
         until there's no more source string to read, or until we've reached the
         frame end.

         On calling LZ4F_decompress, source_read is set to the remaining length
         of source available to read. On return, source_read is set to the
         actual number of bytes read from source, which may be less than
         available. NB: LZ4F_decompress does not explicitly fail on empty input.

         On calling LZ4F_decompress, destination_write is the number of bytes in
         destination available for writing. On exit, destination_write is set to
         the actual number of bytes written to destination. */
      result = LZ4F_decompress (context, destination_cursor, &destination_write, source_cursor, &source_read, &options);

      if (LZ4F_isError (result)) { return result; }

      destination_written += destination_write;
      source_cursor += source_read;
      source_read = source_end - source_cursor;

      if (result == 0) {
        end_of_frame = 1;
        break;
      }
      else if (source_cursor == source_end) { break; }
      else if (destination_written == decompressed_length) { break; }
      destination_cursor = destination + destination_written;
      destination_write = decompressed_length - destination_written;
    }

  if (result > 0) { return 20001; }
  if (end_of_frame != 1) { return 20002; }
  if (LZ4F_isError (result)) { return 20003; }

  return 0;
}



// Started on streaming compression and then realized it has some
// restrictions that make it pretty hard to wrap in Haskell. Read
// the docs for LZ4_compress_fast_continue in lz4.h before trying
// to do this. Basically, if any attempt to do this is ever made to
// do this, the only way forward is to use UnliftedArray ByteArray
// and do the whole thing in one big uninterruptible FFI call since
// nothing is allowed to move. But even then, decompression speed
// will be negatively impacted since you might end up with a bunch
// of small LZ4 chunks. And once you've got more than one LZ4 chunk,
// you have to start thinking about framing too.
//
// HsInt hs_compress_fast_chunks_start
//   ( LZ4_stream_t* ctx
//   , const char* src
//   , HsInt soff
//   , char* dst
//   , HsInt doff
//   , HsInt slen
//   , HsInt dstCapacity
//   , HsInt acceleration
//   ) {
//   int result;
//   result = LZ4_compress_fast_extState(ctx, src+soff, dest+doff, slen, dstCapacity, acceleration);
//   return result;
// }
//
// HsInt hs_compress_fast_chunks_continue
//   ( LZ4_stream_t* ctx
//   , const char* src
//   , HsInt soff
//   , char* dst
//   , HsInt doff
//   , HsInt slen
//   , HsInt dstCapacity
//   , HsInt acceleration
//   ) {
//   int result;
//   result = LZ4_compress_fast_continue(ctx, src+soff, dest+doff, slen, dstCapacity, acceleration);
//   return result;
// }