netcdf-c/plugins/H5Zbzip2.c
Dennis Heimbigner d3b309722e re: gh issue https://github.com/Unidata/netcdf-c/issues/911
I took Ed's advice and moved the plugin stuff to its own
top-level directory. This is an attempt to solve the problem of
copying files that we have experienced. In any case, it will
serve as a place to stick additional plugins.
2018-04-21 20:10:47 -06:00

182 lines
5.3 KiB
C

#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <hdf5.h>
/* Older versions of the hdf library may define H5PL_type_t here */
#include <H5PLextern.h>
#ifndef DLL_EXPORT
#define DLL_EXPORT
#endif
#include "h5bzip2.h"
const H5Z_class2_t H5Z_BZIP2[1] = {{
H5Z_CLASS_T_VERS, /* H5Z_class_t version */
(H5Z_filter_t)H5Z_FILTER_BZIP2, /* Filter id number */
1, /* encoder_present flag (set to true) */
1, /* decoder_present flag (set to true) */
"bzip2", /* Filter name for debugging */
(H5Z_can_apply_func_t)H5Z_bzip2_can_apply, /* The "can apply" callback */
NULL, /* The "set local" callback */
(H5Z_func_t)H5Z_filter_bzip2, /* The actual filter function */
}};
/* External Discovery Functions */
H5PL_type_t
H5PLget_plugin_type(void)
{
return H5PL_TYPE_FILTER;
}
const void*
H5PLget_plugin_info(void)
{
return H5Z_BZIP2;
}
/* Make this explicit */
/*
* The "can_apply" callback returns positive a valid combination, zero for an
* invalid combination and negative for an error.
*/
htri_t
H5Z_bzip2_can_apply(hid_t dcpl_id, hid_t type_id, hid_t space_id)
{
return 1; /* Assume it can always apply */
}
size_t H5Z_filter_bzip2(unsigned int flags, size_t cd_nelmts,
const unsigned int cd_values[], size_t nbytes,
size_t *buf_size, void **buf)
{
char *outbuf = NULL;
size_t outbuflen, outdatalen;
int ret;
if (flags & H5Z_FLAG_REVERSE) {
/** Decompress data.
**
** This process is troublesome since the size of uncompressed data
** is unknown, so the low-level interface must be used.
** Data is decompressed to the output buffer (which is sized
** for the average case); if it gets full, its size is doubled
** and decompression continues. This avoids repeatedly trying to
** decompress the whole block, which could be really inefficient.
**/
bz_stream stream;
char *newbuf = NULL;
size_t newbuflen;
/* Prepare the output buffer. */
outbuflen = nbytes * 3 + 1; /* average bzip2 compression ratio is 3:1 */
outbuf = malloc(outbuflen);
if (outbuf == NULL) {
fprintf(stderr, "memory allocation failed for bzip2 decompression\n");
goto cleanupAndFail;
}
/* Use standard malloc()/free() for internal memory handling. */
stream.bzalloc = NULL;
stream.bzfree = NULL;
stream.opaque = NULL;
/* Start decompression. */
ret = BZ2_bzDecompressInit(&stream, 0, 0);
if (ret != BZ_OK) {
fprintf(stderr, "bzip2 decompression start failed with error %d\n", ret);
goto cleanupAndFail;
}
/* Feed data to the decompression process and get decompressed data. */
stream.next_out = outbuf;
stream.avail_out = outbuflen;
stream.next_in = *buf;
stream.avail_in = nbytes;
do {
ret = BZ2_bzDecompress(&stream);
if (ret < 0) {
fprintf(stderr, "BUG: bzip2 decompression failed with error %d\n", ret);
goto cleanupAndFail;
}
if (ret != BZ_STREAM_END && stream.avail_out == 0) {
/* Grow the output buffer. */
newbuflen = outbuflen * 2;
newbuf = realloc(outbuf, newbuflen);
if (newbuf == NULL) {
fprintf(stderr, "memory allocation failed for bzip2 decompression\n");
goto cleanupAndFail;
}
stream.next_out = newbuf + outbuflen; /* half the new buffer behind */
stream.avail_out = outbuflen; /* half the new buffer ahead */
outbuf = newbuf;
outbuflen = newbuflen;
}
} while (ret != BZ_STREAM_END);
/* End compression. */
outdatalen = stream.total_out_lo32;
ret = BZ2_bzDecompressEnd(&stream);
if (ret != BZ_OK) {
fprintf(stderr, "bzip2 compression end failed with error %d\n", ret);
goto cleanupAndFail;
}
} else {
/** Compress data.
**
** This is quite simple, since the size of compressed data in the worst
** case is known and it is not much bigger than the size of uncompressed
** data. This allows us to use the simplified one-shot interface to
** compression.
**/
unsigned int odatalen; /* maybe not the same size as outdatalen */
int blockSize100k = 9;
/* Get compression block size if present. */
if (cd_nelmts > 0) {
blockSize100k = cd_values[0];
if (blockSize100k < 1 || blockSize100k > 9) {
fprintf(stderr, "invalid compression block size: %d\n", blockSize100k);
goto cleanupAndFail;
}
}
/* Prepare the output buffer. */
outbuflen = nbytes + nbytes / 100 + 600; /* worst case (bzip2 docs) */
outbuf = malloc(outbuflen);
if (outbuf == NULL) {
fprintf(stderr, "memory allocation failed for bzip2 compression\n");
goto cleanupAndFail;
}
/* Compress data. */
odatalen = outbuflen;
ret = BZ2_bzBuffToBuffCompress(outbuf, &odatalen, *buf, nbytes,
blockSize100k, 0, 0);
outdatalen = odatalen;
if (ret != BZ_OK) {
fprintf(stderr, "bzip2 compression failed with error %d\n", ret);
goto cleanupAndFail;
}
}
/* Always replace the input buffer with the output buffer. */
free(*buf);
*buf = outbuf;
*buf_size = outbuflen;
return outdatalen;
cleanupAndFail:
if (outbuf)
free(outbuf);
return 0;
}