And use it for automatic output compression in FU, as (potentially) faster alternative to Compress::Raw::Zlib. Was also planning to maybe add support for Zstd or Brotli, but given the performance of libdeflate, I'm not sure that's really necessary. Brotli does tend to do a better job at compressing HTML, though.
128 lines
4.4 KiB
C
128 lines
4.4 KiB
C
static const char *fugz_imps[] = {"", "libdeflate", "zlib-ng", "zlib"};
|
|
static int fugz_imp = -1;
|
|
|
|
|
|
/* zlib & zlib-ng */
|
|
|
|
typedef struct {
|
|
const char *next_in;
|
|
unsigned int avail_in;
|
|
unsigned long total_in;
|
|
char *next_out;
|
|
unsigned int avail_out;
|
|
unsigned long total_out;
|
|
const char *msg;
|
|
struct internal_state *state;
|
|
void *zalloc;
|
|
void *zfree;
|
|
void *opaque;
|
|
int data_type;
|
|
unsigned long adler;
|
|
unsigned long reserved;
|
|
} z_stream;
|
|
|
|
static int (*deflate)(z_stream *, int);
|
|
static int (*deflateEnd)(z_stream *);
|
|
static int (*deflateInit2)(z_stream *, int, int, int, int, int);
|
|
static int (*deflateInit2_)(z_stream *, int, int, int, int, int, const char *, int);
|
|
static unsigned long (*compressBound)(unsigned long);
|
|
|
|
|
|
/* libdeflate */
|
|
|
|
static struct libdeflate_compressor *fugz_ld_ctx;
|
|
static int fugz_ld_comp = -1;
|
|
|
|
static struct libdeflate_compressor *(*libdeflate_alloc_compressor)(int);
|
|
static void (*libdeflate_free_compressor)(struct libdeflate_compressor *);
|
|
static size_t (*libdeflate_gzip_compress_bound)(struct libdeflate_compressor *, size_t);
|
|
static size_t (*libdeflate_gzip_compress)(struct libdeflate_compressor *, const void *, size_t, void *, size_t);
|
|
|
|
|
|
|
|
static const char *fugz_lib() {
|
|
if (fugz_imp >= 0) goto done;
|
|
|
|
void *handle;
|
|
if ((handle = dlopen("libdeflate.so", RTLD_LAZY))) {
|
|
if ((libdeflate_alloc_compressor = dlsym(handle, "libdeflate_alloc_compressor"))
|
|
&& (libdeflate_free_compressor = dlsym(handle, "libdeflate_free_compressor"))
|
|
&& (libdeflate_gzip_compress_bound = dlsym(handle, "libdeflate_gzip_compress_bound"))
|
|
&& (libdeflate_gzip_compress = dlsym(handle, "libdeflate_gzip_compress"))) {
|
|
fugz_imp = 1;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
int i;
|
|
for (i=2; i<=3; i++) {
|
|
if ((handle = dlopen(i == 2 ? "libz-ng.so" : "libz.so", RTLD_LAZY))) {
|
|
if (((deflate = dlsym(handle, "zng_deflate")) || (deflate = dlsym(handle, "deflate")))
|
|
&& ((deflateEnd = dlsym(handle, "zng_deflateEnd")) || (deflateEnd = dlsym(handle, "deflateEnd")))
|
|
&& ((deflateInit2 = dlsym(handle, "zng_deflateInit2")) || (deflateInit2_ = dlsym(handle, "deflateInit2_")))
|
|
&& ((compressBound = dlsym(handle, "zng_compressBound")) || (compressBound = dlsym(handle, "compressBound")))) {
|
|
fugz_imp = i;
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
fugz_imp = 0;
|
|
|
|
done:
|
|
return fugz_imps[fugz_imp];
|
|
}
|
|
|
|
|
|
static SV *fugz_compress_ld(pTHX_ int level, const char *bytes, size_t inlen) {
|
|
if (fugz_ld_comp != level) {
|
|
if (fugz_ld_ctx) libdeflate_free_compressor(fugz_ld_ctx);
|
|
fugz_ld_ctx = NULL;
|
|
fugz_ld_comp = level;
|
|
}
|
|
if (!fugz_ld_ctx) fugz_ld_ctx = libdeflate_alloc_compressor(level);
|
|
|
|
size_t outlen = libdeflate_gzip_compress_bound(fugz_ld_ctx, inlen);
|
|
SV *out = sv_2mortal(newSV(outlen));
|
|
SvPOK_only(out);
|
|
size_t len = libdeflate_gzip_compress(fugz_ld_ctx, bytes, inlen, SvPVX(out), outlen);
|
|
if (!len) fu_confess("Libdeflate compression failed"); /* Shouldn't happen */
|
|
SvCUR_set(out, len);
|
|
return out;
|
|
}
|
|
|
|
|
|
static SV *fugz_compress_zlib(pTHX_ int level, const char *bytes, size_t inlen) {
|
|
z_stream stream;
|
|
memset(&stream, 0, sizeof(stream));
|
|
|
|
int r = deflateInit2
|
|
? deflateInit2(&stream, level > 9 ? 9 : level, 8, 16+15, 9, 0)
|
|
: deflateInit2_(&stream, level > 9 ? 9 : level, 8, 16+15, 9, 0, "1.3.1", (int)sizeof(stream));
|
|
if (r) fu_confess("Zlib compression failed (%d)", r);
|
|
|
|
stream.avail_out = compressBound(inlen) + 64; /* compressBound() does not include the gzip header */
|
|
SV *out = sv_2mortal(newSV(stream.avail_out));
|
|
SvPOK_only(out);
|
|
stream.next_out = SvPVX(out);
|
|
stream.next_in = bytes;
|
|
stream.avail_in = inlen;
|
|
|
|
if ((r = deflate(&stream, 4)) != 1) fu_confess("Zlib compression failed (%d)", r);
|
|
|
|
SvCUR_set(out, stream.total_out);
|
|
deflateEnd(&stream);
|
|
return out;
|
|
}
|
|
|
|
|
|
static SV *fugz_compress(pTHX_ IV level, SV *in) {
|
|
if (level < 0 || level > 12) fu_confess("Invalid compression level: %"IVdf, level);
|
|
if (!*fugz_lib()) fu_confess("Unable to load a suitable compression library");
|
|
|
|
STRLEN inlen;
|
|
const char *bytes = SvPVbyte(in, inlen);
|
|
|
|
if (fugz_imp == 1) return fugz_compress_ld(aTHX_ level, bytes, inlen);
|
|
else return fugz_compress_zlib(aTHX_ level, bytes, inlen);
|
|
return &PL_sv_undef;
|
|
}
|