The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#define PERL_NO_GET_CONTEXT

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#define NEED_sv_2pvbyte
#include "ppport.h"

#include <lz4.h>
#include <lz4hc.h>

MODULE = Compress::LZ4    PACKAGE = Compress::LZ4

PROTOTYPES: ENABLE

SV *
compress (sv, level=0)
    SV *sv
    IV level
ALIAS:
    compress_hc = 1
    lz4_compress = 2
    lz4_compress_hc = 3
PREINIT:
    char *src, *dest;
    STRLEN src_len, dest_len;
    int hc;
CODE:
    SvGETMAGIC(sv);
    if (SvROK(sv) && ! SvAMAGIC(sv)) {
        sv = SvRV(sv);
        SvGETMAGIC(sv);
    }
    if (! SvOK(sv))
        XSRETURN_NO;
    src = SvPVbyte(sv, src_len);
    if (! src_len)
        XSRETURN_NO;
    dest_len = LZ4_compressBound(src_len);
    if (2 > ix)
        dest_len += 4;
    RETVAL = newSV(dest_len);
    dest = SvPVX(RETVAL);
    if (! dest)
        XSRETURN_UNDEF;

    hc = ix & 1;
    if (2 > ix) {
        /* Add the length header as 4 bytes in little endian. */
        dest[0] = src_len       & 0xff;
        dest[1] = (src_len>> 8) & 0xff;
        dest[2] = (src_len>>16) & 0xff;
        dest[3] = (src_len>>24) & 0xff;

        dest_len = hc ? LZ4_compress_HC(src, dest + 4, src_len, dest_len, level)
                    : LZ4_compress_fast(src, dest + 4, src_len, dest_len, level);
        dest_len += 4;
    }
    else {
        dest_len = hc ? LZ4_compress_HC(src, dest, src_len, dest_len, level)
                    : LZ4_compress_fast(src, dest, src_len, dest_len, level);
    }

    if (! dest_len) {
        SvREFCNT_dec(RETVAL);
        XSRETURN_UNDEF;
    }
    SvCUR_set(RETVAL, dest_len);
    SvPOK_on(RETVAL);
OUTPUT:
    RETVAL

SV *
decompress (sv, len=0)
    SV *sv
    int len
ALIAS:
    uncompress = 1
    lz4_decompress = 2
    lz4_uncompress = 3
PREINIT:
    char *src, *dest;
    STRLEN src_len, dest_len;
    int ret;
CODE:
    PERL_UNUSED_VAR(ix);  /* -W */
    SvGETMAGIC(sv);
    if (SvROK(sv) && ! SvAMAGIC(sv)) {
        sv = SvRV(sv);
        SvGETMAGIC(sv);
    }
    if (! SvOK(sv))
        XSRETURN_NO;
    src = SvPVbyte(sv, src_len);
    if (! src_len )
        XSRETURN_NO;

    if (1 == items) {
        if (src_len < 5)
            XSRETURN_NO;

        /* Decode the length header. */
        dest_len = (src[0] & 0xff) | (src[1] & 0xff) << 8 | (src[2] & 0xff) << 16
                                | (src[3] & 0xff) << 24;
    }
    else {
        if (0 >= len)
            XSRETURN_NO;
        dest_len = len;
    }

    RETVAL = newSV(dest_len);
    dest = SvPVX(RETVAL);
    if (! dest)
        XSRETURN_UNDEF;

    if (1 == items)
        ret = LZ4_decompress_safe(src + 4, dest, src_len - 4, dest_len);
    else
        ret = LZ4_decompress_safe(src, dest, src_len, dest_len);

    if (0 > ret) {
        SvREFCNT_dec(RETVAL);
        XSRETURN_UNDEF;
    }
    SvCUR_set(RETVAL, ret);
    SvPOK_on(RETVAL);
OUTPUT:
    RETVAL