mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-12-21 08:39:46 +08:00
126b3f9423
re: https://github.com/Unidata/netcdf-c/issues/2294 Ed Hartnett suggested that the netcdf library installation process be extended to install the standard filters into a user specified location. The user can then set HDF5_PLUGIN_PATH to that location. This PR provides that capability using: ```` configure option: --with-plugin-dir=<absolute directory path> cmake option: -DPLUGIN_INSTALL_DIR=<absolute directory path> ```` Currently, the following plugins are always installed, if available: bzip2, zstd, blosc. If NCZarr is enabled, then additional plugins are installed: fletcher32, shuffle, deflate, szip. Additionally, the necessary codec support is installed for each of the above filters that is installed. ## Changes: 1. Cleanup handling of built-in bzip2. 2. Add documentation to docs/filters.md 3. Re-factor the NCZarr codec libraries 4. Add a test, although it can only be exercised after the library is installed, so it cannot be used during normal testing. 5. Cleanup use of HDF5_PLUGIN_PATH in the filter test cases.
217 lines
6.3 KiB
C
217 lines
6.3 KiB
C
#include "config.h"
|
|
#include <sys/types.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
|
|
#include "netcdf_filter_build.h"
|
|
|
|
/* WARNING:
|
|
Starting with HDF5 version 1.10.x, the plugin code MUST be
|
|
careful when using the standard *malloc()*, *realloc()*, and
|
|
*free()* function.
|
|
|
|
In the event that the code is allocating, reallocating, or
|
|
free'ing memory that either came from or will be exported to the
|
|
calling HDF5 library, then one MUST use the corresponding HDF5
|
|
functions *H5allocate_memory()*, *H5resize_memory()*,
|
|
*H5free_memory()* [5] to avoid memory failures.
|
|
|
|
Additionally, if your filter code leaks memory, then the HDF5 library
|
|
will generate an error.
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "netcdf_filter_build.h"
|
|
#include <netcdf_json.h>
|
|
|
|
#include "h5bzip2.h"
|
|
|
|
/* Forward */
|
|
static htri_t H5Z_bzip2_can_apply(hid_t dcpl_id, hid_t type_id, hid_t space_id);
|
|
static size_t H5Z_filter_bzip2(unsigned flags,size_t cd_nelmts,const unsigned cd_values[],
|
|
size_t nbytes,size_t *buf_size,void**buf);
|
|
|
|
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 */
|
|
DLLEXPORT
|
|
H5PL_type_t
|
|
H5PLget_plugin_type(void)
|
|
{
|
|
return H5PL_TYPE_FILTER;
|
|
}
|
|
|
|
DLLEXPORT
|
|
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.
|
|
*/
|
|
static 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 */
|
|
}
|
|
|
|
static 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 = H5allocate_memory(outbuflen,0);
|
|
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 = H5resize_memory(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 = H5allocate_memory(outbuflen,0);
|
|
|
|
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. */
|
|
H5free_memory(*buf);
|
|
|
|
*buf = outbuf;
|
|
*buf_size = outbuflen;
|
|
return outdatalen;
|
|
|
|
cleanupAndFail:
|
|
if (outbuf)
|
|
H5free_memory(outbuf);
|
|
return 0;
|
|
}
|
|
|