mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-12-27 08:49:16 +08:00
446348ed18
re: PR https://github.com/Unidata/netcdf-c/pull/2088 re: PR https://github.com/Unidata/netcdf-c/pull/2130 replaces: https://github.com/Unidata/netcdf-c/pull/2140 Changes: * Add NCZarr-specific quantize functions to the dispatch table. * Copy (modified) quantize code from libhdf5 to NCZarr * Add quantize invocation to zvar.c * Add support for _QuantizeBitgroomNumberOfSignificantDigits and _QuantizeGranularBitgroomNumberOfSignificantDigits to ncgen. * Modify nc_test4/tst_quantize.c to allow it to be used both for hdf5 and for nczarr. * Make dap4 properly handle quantize functions in dispatch table. * Add quantize attribute support to ncgen. Other changes: * Caught and fixed some S3 problems * Fixed some nczarr fillvalue problems. * Fixed some nczarr cache problems. * Cleanup some flaws in libdispatch/dinfermodel.c * Allow byterange requests to S3 be readable by dinfermodel.c/check_file_type * Remove the libnczarr ztracedispatch code (big change).
1591 lines
48 KiB
C
1591 lines
48 KiB
C
/* Copyright 2003-2018, University Corporation for Atmospheric
|
|
* Research. See the COPYRIGHT file for copying and redistribution
|
|
* conditions.
|
|
*/
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* Copyright by The HDF Group. *
|
|
* Copyright by the Board of Trustees of the University of Illinois. *
|
|
* All rights reserved. *
|
|
* *
|
|
* This file is part of HDF5. The full HDF5 copyright notice, including *
|
|
* terms governing use, modification, and redistribution, is contained in *
|
|
* the COPYING file, which can be found at the root of the source code *
|
|
* distribution tree, or in https://support.hdfgroup.org/ftp/hdf5/releases. *
|
|
* If you do not have access to either file, you may request a copy from *
|
|
* help@hdfgroup.org. *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
/**
|
|
* @file @internal Internal functions for filters
|
|
*
|
|
* This file contains functions internal to the netcdf4 library. None of
|
|
* the functions in this file are exposed in the exetnal API. These
|
|
* functions all relate to the manipulation of netcdf-4 filters
|
|
*
|
|
* @author Dennis Heimbigner
|
|
*
|
|
* This file is very similar to libhdf5/hdf5filters.c, so changes
|
|
* should be propagated if needed.
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <stdlib.h>
|
|
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#ifdef HAVE_DIRENT_H
|
|
#include <dirent.h>
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#include "zincludes.h"
|
|
#include "zfilter.h"
|
|
#include "ncpathmgr.h"
|
|
#include "ncpoco.h"
|
|
#include "netcdf_filter.h"
|
|
#include "netcdf_filter_build.h"
|
|
#include "netcdf_aux.h"
|
|
|
|
#undef DEBUG
|
|
#undef DEBUGF
|
|
#undef DEBUGL
|
|
|
|
#define NULLIFY(x) ((x)?(x):"NULL")
|
|
|
|
/* Hold the loaded filter plugin information */
|
|
typedef struct NCZ_Plugin {
|
|
struct HDF5API {
|
|
const H5Z_class2_t* filter;
|
|
NCPSharedLib* hdf5lib; /* source of the filter */
|
|
} hdf5;
|
|
struct CodecAPI {
|
|
const NCZ_codec_t* codec;
|
|
NCPSharedLib* codeclib; /* of the source codec; null if same as hdf5 */
|
|
} codec;
|
|
} NCZ_Plugin;
|
|
|
|
/* The NC_VAR_INFO_T->filters field is an NClist of this struct */
|
|
/*
|
|
Each filter can have two parts: HDF5 and Codec.
|
|
The NC_VAR_INFO_T.filters list only holds entries where both the HDF5 info
|
|
and the codec info are defined.
|
|
The NCZ_VAR_INFO_T.codecs list holds the codec info when reading a Zarr file.
|
|
Note that it is not possible to have an entry on the filters list that does not
|
|
have both HDF5 and codec. This is because nc_def_var_filter will fail if the codec
|
|
part is not available. If a codec is read from a file and there is no available
|
|
corresponding HDF5 implementation, then that codec will not appear in the filters list.
|
|
It is possible that some subset of the codecs do have a corresponding HDF5, but we
|
|
enforce the rule that no entries go into the filters list unless all are defined.
|
|
It is still desirable for a user to be able to see what filters and codecs are defined
|
|
for a variable. This is accommodated by providing two special attributes:
|
|
1, "_Filters" attribute shows the HDF5 filters defined on the variable, if any.
|
|
2, "_Codecs" attribute shows the codecs defined on the variable; for zarr, this list
|
|
should always be defined.
|
|
*/
|
|
|
|
/* Codec Info */
|
|
typedef struct NCZ_Codec {
|
|
char* id; /**< The NumCodecs ID */
|
|
char* codec; /**< The Codec from the file; NULL if creating */
|
|
} NCZ_Codec;
|
|
|
|
static NCZ_Codec codec_empty = {NULL, NULL};
|
|
|
|
static void
|
|
ncz_codec_clear(NCZ_Codec* codec) {
|
|
nullfree(codec->id); nullfree(codec->codec);
|
|
*codec = codec_empty;
|
|
}
|
|
|
|
typedef struct NCZ_Params {size_t nparams; unsigned* params;} NCZ_Params;
|
|
|
|
/* HDF5 Info */
|
|
typedef struct NCZ_HDF5 {
|
|
unsigned id; /**< HDF5 id corresponding to filterid. */
|
|
NCZ_Params visible;
|
|
NCZ_Params working;
|
|
} NCZ_HDF5;
|
|
|
|
static NCZ_HDF5 hdf5_empty = {0, {0,NULL}, {0,NULL}};
|
|
|
|
static void
|
|
ncz_hdf5_clear(NCZ_HDF5* h) {
|
|
nullfree(h->visible.params);
|
|
nullfree(h->working.params);
|
|
*h = hdf5_empty;
|
|
}
|
|
|
|
typedef struct NCZ_Filter {
|
|
int flags; /**< Flags describing state of this filter. */
|
|
# define FLAG_VISIBLE 1 /* If set, then visible parameters are defined */
|
|
# define FLAG_WORKING 2 /* If set, then WORKING parameters are defined */
|
|
# define FLAG_CODEC 4 /* If set, then visbile parameters come from an existing codec string */
|
|
# define FLAG_HDF5 8 /* If set, => visible parameters came from nc_def_var_filter */
|
|
# define FLAG_NEWVISIBLE 16 /* If set, => visible parameters were modified */
|
|
NCZ_HDF5 hdf5;
|
|
NCZ_Codec codec;
|
|
struct NCZ_Plugin* plugin; /**< Implementation of this filter. */
|
|
} NCZ_Filter;
|
|
|
|
/* WARNING: GLOBAL DATA */
|
|
/* All possible HDF5 filter plugins */
|
|
/* Convert to linked list or hash table or equivalent since very sparse */
|
|
NCZ_Plugin* loaded_plugins[H5Z_FILTER_MAX];
|
|
int loaded_plugins_max = -1;
|
|
|
|
static NCZ_codec_t** codec_defaults; /* NULL terminated */
|
|
static NCPSharedLib* default_lib; /* source of the defaults */
|
|
|
|
static int NCZ_filter_initialized = 0;
|
|
|
|
/**************************************************/
|
|
|
|
#ifdef ZTRACING
|
|
static const char*
|
|
NCJtrace(const NCjson* j)
|
|
{
|
|
static char jstat[4096];
|
|
char* js = NULL;
|
|
jstat[0] = '\0';
|
|
if(j) {
|
|
(void)NCJunparse(j,0,&js);
|
|
if(js) strlcat(jstat,js,sizeof(jstat));
|
|
nullfree(js);
|
|
}
|
|
return jstat;
|
|
}
|
|
|
|
#define IEXISTS(x,p) (((x) && *(x)? (*(x))-> p : 0xffffffff))
|
|
#define SEXISTS(x,p) (((x) && *(x)? (*(x))-> p : "null"))
|
|
#endif
|
|
|
|
|
|
#ifdef DEBUGF
|
|
|
|
const char*
|
|
printplugin(const NCZ_Plugin* plugin)
|
|
{
|
|
static char plbuf[4096];
|
|
char plbuf2[2000];
|
|
char plbuf1[2000];
|
|
|
|
if(plugin == NULL) return "plugin=NULL";
|
|
plbuf2[0] = '\0'; plbuf1[0] = '\0';
|
|
if(plugin->hdf5.filter)
|
|
snprintf(plbuf1,sizeof(plbuf1),"hdf5={id=%u name=%s}",plugin->hdf5.filter->id,plugin->hdf5.filter->name);
|
|
if(plugin->codec.codec)
|
|
snprintf(plbuf2,sizeof(plbuf2),"codec={codecid=%s hdf5id=%u}",plugin->codec.codec->codecid,plugin->codec.codec->hdf5id);
|
|
snprintf(plbuf,sizeof(plbuf),"plugin={%s %s}",plbuf1,plbuf2);
|
|
return plbuf;
|
|
}
|
|
|
|
static const char*
|
|
printparams(size_t nparams, const unsigned* params)
|
|
{
|
|
static char ppbuf[4096];
|
|
|
|
if(nparams == 0)
|
|
snprintf(ppbuf,sizeof(ppbuf),"{0,%p}",params);
|
|
else
|
|
snprintf(ppbuf,sizeof(ppbuf),"{%u %s}",(unsigned)nparams,nczprint_paramvector(nparams,params));
|
|
return ppbuf;
|
|
}
|
|
|
|
static const char*
|
|
printnczparams(const NCZ_Params p)
|
|
{
|
|
return printparams(p.nparams,p.params);
|
|
}
|
|
|
|
static const char*
|
|
printcodec(const NCZ_Codec c)
|
|
{
|
|
static char pcbuf[4096];
|
|
snprintf(pcbuf,sizeof(pcbuf),"{id=%s codec=%s}",
|
|
c.id,NULLIFY(c.codec));
|
|
return pcbuf;
|
|
}
|
|
|
|
static const char*
|
|
printhdf5(const NCZ_HDF5 h)
|
|
{
|
|
static char phbuf[4096];
|
|
snprintf(phbuf,sizeof(phbuf),"{id=%u visible=%s working=%s}",
|
|
h.id, printnczparams(h.visible), printnczparams(h.working));
|
|
return phbuf;
|
|
}
|
|
|
|
static const char*
|
|
printfilter(const NCZ_Filter* f)
|
|
{
|
|
static char pfbuf[4096];
|
|
|
|
if(f == NULL) return "NULL";
|
|
snprintf(pfbuf,sizeof(pfbuf),"{flags=%d hdf5=%s codec=%s plugin=%p}",
|
|
f->flags, printhdf5(f->hdf5),printcodec(f->codec),f->plugin);
|
|
return pfbuf;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/* Forward */
|
|
static int NCZ_load_all_plugins(void);
|
|
static int NCZ_load_plugin_dir(const char* path);
|
|
static int NCZ_load_plugin(const char* path, NCZ_Plugin** plugp);
|
|
static int NCZ_unload_plugin(NCZ_Plugin* plugin);
|
|
static int NCZ_plugin_loaded(int filterid, NCZ_Plugin** pp);
|
|
static int NCZ_plugin_save(int filterid, NCZ_Plugin* p);
|
|
static int NCZ_filter_free(NCZ_Filter* spec);
|
|
static int NCZ_filter_hdf5_clear(NCZ_HDF5* spec);
|
|
static int NCZ_filter_codec_clear(NCZ_Codec* spec);
|
|
static int NCZ_filter_lookup(NC_VAR_INFO_T* var, unsigned int id, struct NCZ_Filter** specp);
|
|
|
|
static int getentries(const char* path, NClist* contents);
|
|
static int NCZ_split_plugin_path(const char* path0, NClist* list);
|
|
|
|
static int ensure_working(const NC_VAR_INFO_T* var, NCZ_Filter* filter);
|
|
|
|
static int paramnczclone(NCZ_Params* dst, const NCZ_Params* src);
|
|
static int paramclone(size_t nparams, unsigned** dstp, const unsigned* src);
|
|
|
|
/**************************************************/
|
|
/**
|
|
* @file
|
|
* @internal
|
|
* Internal netcdf nczarr filter functions.
|
|
*
|
|
* This file contains functions internal to the libnczarr library.
|
|
* None of the functions in this file are exposed in the exernal API. These
|
|
* functions all relate to the manipulation of netcdf-4's var->filters list.
|
|
*
|
|
* @author Dennis Heimbigner
|
|
*/
|
|
|
|
int
|
|
NCZ_filter_freelist(NC_VAR_INFO_T* var)
|
|
{
|
|
int i, stat=NC_NOERR;
|
|
NClist* filters = (NClist*)var->filters;
|
|
|
|
ZTRACE(6,"var=%s",var->hdr.name);
|
|
if(filters == NULL) goto done;
|
|
/* Free the filter list elements */
|
|
for(i=0;i<nclistlength(filters);i++) {
|
|
struct NCZ_Filter* spec = nclistget(filters,i);
|
|
if((stat = NCZ_filter_free(spec))) goto done;
|
|
}
|
|
nclistfree(filters);
|
|
var->filters = NULL;
|
|
done:
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
static int
|
|
NCZ_filter_free(NCZ_Filter* spec)
|
|
{
|
|
if(spec == NULL) return NC_NOERR;
|
|
NCZ_filter_hdf5_clear(&spec->hdf5);
|
|
NCZ_filter_codec_clear(&spec->codec);
|
|
free(spec);
|
|
return NC_NOERR;
|
|
}
|
|
|
|
static int
|
|
NCZ_filter_hdf5_clear(NCZ_HDF5* spec)
|
|
{
|
|
ZTRACE(6,"spec=%d",spec->id);
|
|
if(spec == NULL) goto done;
|
|
nullfree(spec->visible.params);
|
|
nullfree(spec->working.params);
|
|
done:
|
|
return ZUNTRACE(NC_NOERR);
|
|
}
|
|
|
|
static int
|
|
NCZ_filter_codec_clear(NCZ_Codec* spec)
|
|
{
|
|
ZTRACE(6,"spec=%d",(spec?spec->id:"null"));
|
|
if(spec == NULL) goto done;
|
|
nullfree(spec->id);
|
|
nullfree(spec->codec);
|
|
done:
|
|
return ZUNTRACE(NC_NOERR);
|
|
}
|
|
|
|
/* From NCZ_def_var_filter */
|
|
int
|
|
NCZ_addfilter(NC_FILE_INFO_T* file, NC_VAR_INFO_T* var, unsigned int id, size_t nparams, const unsigned int* params)
|
|
{
|
|
int stat = NC_NOERR;
|
|
struct NCZ_Filter* fi = NULL;
|
|
NCZ_Plugin* plugin = NULL;
|
|
NCZ_HDF5 hdf5 = hdf5_empty;
|
|
|
|
ZTRACE(6,"file=%s var=%s id=%u nparams=%u params=%p",file->hdr.name,var->hdr.name,id,nparams,params);
|
|
|
|
if(nparams > 0 && params == NULL)
|
|
{stat = NC_EINVAL; goto done;}
|
|
|
|
if(var->filters == NULL) var->filters = (void*)nclistnew();
|
|
|
|
/* Before anything else, find the matching plugin */
|
|
if((stat = NCZ_plugin_loaded(id,&plugin))) goto done;
|
|
if(plugin == NULL || plugin->codec.codec == NULL) { /* fail */
|
|
ZLOG(NCLOGWARN,"no such plugin: %u",(unsigned)id);
|
|
stat = NC_ENOFILTER;
|
|
goto done;
|
|
}
|
|
|
|
/* Fill in the hdf5 */
|
|
hdf5 = hdf5_empty;
|
|
hdf5.id = id;
|
|
/* Capture the visible parameters */
|
|
hdf5.visible.nparams = nparams;
|
|
if(nparams > 0) {
|
|
if((stat = paramclone(nparams,&hdf5.visible.params,params))) goto done;
|
|
}
|
|
|
|
/* Find the NCZ_Filter */
|
|
if((stat=NCZ_filter_lookup(var,id,&fi))) goto done;
|
|
if(fi != NULL) {
|
|
if(fi->plugin != plugin)
|
|
{stat = NC_EINTERNAL; goto done;}
|
|
} else {
|
|
stat = NC_NOERR;
|
|
if((fi = calloc(1,sizeof(struct NCZ_Filter))) == NULL)
|
|
{stat = NC_ENOMEM; goto done;}
|
|
fi->plugin = plugin;
|
|
nclistpush((NClist*)var->filters, fi);
|
|
}
|
|
|
|
/* (over)write the HDF5 parameters */
|
|
nullfree(fi->hdf5.visible.params);
|
|
nullfree(fi->hdf5.working.params);
|
|
fi->hdf5.working.nparams = 0;
|
|
fi->hdf5.working.params = NULL;
|
|
fi->hdf5 = hdf5;
|
|
hdf5 = hdf5_empty;
|
|
fi->flags |= FLAG_VISIBLE;
|
|
|
|
fi = NULL; /* either way,its in the var->filters list */
|
|
|
|
done:
|
|
if(fi) NCZ_filter_free(fi);
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
int
|
|
NCZ_filter_remove(NC_VAR_INFO_T* var, unsigned int id)
|
|
{
|
|
int k, stat = NC_NOERR;
|
|
NClist* flist = (NClist*)var->filters;
|
|
|
|
ZTRACE(6,"var=%s id=%u",var->hdr.name,id);
|
|
/* Walk backwards */
|
|
for(k=nclistlength(flist)-1;k>=0;k--) {
|
|
struct NCZ_Filter* f = (struct NCZ_Filter*)nclistget(flist,k);
|
|
if(f->hdf5.id == id) {
|
|
/* Remove from variable */
|
|
nclistremove(flist,k);
|
|
/* Reclaim */
|
|
NCZ_filter_free(f);
|
|
goto done;
|
|
}
|
|
}
|
|
ZLOG(NCLOGERR,"no such filter: %u",(unsigned)id);
|
|
stat = NC_ENOFILTER;
|
|
done:
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
static int
|
|
NCZ_filter_lookup(NC_VAR_INFO_T* var, unsigned int id, struct NCZ_Filter** specp)
|
|
{
|
|
int i;
|
|
NClist* flist = (NClist*)var->filters;
|
|
|
|
|
|
ZTRACE(6,"var=%s id=%u",var->hdr.name,id);
|
|
|
|
if(specp) *specp = NULL;
|
|
if(flist == NULL) {
|
|
if((flist = nclistnew())==NULL)
|
|
return NC_ENOMEM;
|
|
var->filters = (void*)flist;
|
|
}
|
|
for(i=0;i<nclistlength(flist);i++) {
|
|
NCZ_Filter* spec = nclistget(flist,i);
|
|
assert(spec != NULL);
|
|
if(spec->hdf5.id == id) {
|
|
if(specp) *specp = spec;
|
|
break;
|
|
}
|
|
}
|
|
return ZUNTRACEX(NC_NOERR,"spec=%d",IEXISTS(specp,hdf5.id));
|
|
}
|
|
|
|
#if 0
|
|
static int
|
|
NCZ_codec_lookup(NClist* codecs, const char* id, NCZ_Codec** codecp)
|
|
{
|
|
int i;
|
|
|
|
ZTRACE(6,"|codecs|=%u id=%u", (unsigned)nclistlength(codecs), id);
|
|
if(codecp) *codecp = NULL;
|
|
|
|
if(codecs == NULL) return NC_NOERR;
|
|
for(i=0;i<nclistlength(codecs);i++) {
|
|
NCZ_Codec* spec = nclistget(codecs,i);
|
|
assert(spec != NULL);
|
|
if(strcmp(spec->id,id)==0) {
|
|
if(codecp) *codecp = spec;
|
|
break;
|
|
}
|
|
}
|
|
return ZUNTRACEX(NC_NOERR,"codec=%s",SEXISTS(codecp,id));
|
|
}
|
|
|
|
/**
|
|
* @internal Remove a filter from filter list for a variable
|
|
*
|
|
* @param ncid File ID.
|
|
* @param varid Variable ID.
|
|
* @param id filter id to remove
|
|
*
|
|
* @returns ::NC_NOERR No error.
|
|
* @returns ::NC_EBADID Bad ncid.
|
|
* @returns ::NC_ENOTVAR Invalid variable ID.
|
|
* @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is
|
|
* not netCDF-4/NCZARR.
|
|
* @returns ::NC_ELATEDEF Too late to change settings for this variable.
|
|
* @returns ::NC_ENOTINDEFINE Not in define mode.
|
|
* @returns ::NC_EINVAL Invalid input
|
|
* @author Dennis Heimbigner
|
|
*/
|
|
int
|
|
nc_var_filter_remove(int ncid, int varid, unsigned int filterid)
|
|
{
|
|
NC_VAR_INFO_T *var = NULL;
|
|
int stat;
|
|
|
|
/* Get pointer to the var. */
|
|
if ((stat = ncz_find_grp_file_var(ncid, varid, NULL, NULL, &var)))
|
|
return stat;
|
|
assert(var);
|
|
|
|
stat = NC4_nczarr_filter_remove(var,filterid);
|
|
|
|
return stat;
|
|
}
|
|
#endif
|
|
|
|
#ifdef ENABLE_NCZARR_FILTERS
|
|
int
|
|
NCZ_def_var_filter(int ncid, int varid, unsigned int id, size_t nparams,
|
|
const unsigned int* params)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NC *nc;
|
|
NC_FILE_INFO_T* h5 = NULL;
|
|
NC_GRP_INFO_T* grp = NULL;
|
|
NC_VAR_INFO_T* var = NULL;
|
|
NCZ_Filter* oldspec = NULL;
|
|
NCZ_Filter* tmp = NULL;
|
|
int havedeflate = 0;
|
|
int haveszip = 0;
|
|
|
|
ZTRACE(1,"ncid=%d varid=%d id=%u nparams=%u params=%s",ncid,varid,id,(unsigned)nparams,nczprint_paramvector(nparams,params));
|
|
|
|
if((stat = NCZ_filter_initialize())) goto done;
|
|
|
|
if((stat = NC_check_id(ncid,&nc))) return stat;
|
|
assert(nc);
|
|
|
|
/* Find info for this file and group and var, and set pointer to each. */
|
|
if ((stat = ncz_find_grp_file_var(ncid, varid, &h5, &grp, &var)))
|
|
{stat = THROW(stat); goto done;}
|
|
|
|
assert(h5 && var && var->hdr.id == varid);
|
|
|
|
/* If the NCZARR dataset has already been created, then it is too
|
|
* late to set all the extra stuff. */
|
|
if (!(h5->flags & NC_INDEF))
|
|
{stat = THROW(NC_EINDEFINE); goto done;}
|
|
if (!var->ndims)
|
|
{stat = NC_EINVAL; goto done;} /* For scalars, complain */
|
|
if (var->created)
|
|
{stat = THROW(NC_ELATEDEF); goto done;}
|
|
|
|
/* Lookup incoming id to see if already defined */
|
|
if((stat=NCZ_filter_lookup(var,id,&oldspec))) goto done;
|
|
|
|
/* See if deflate &/or szip is defined */
|
|
if((stat = NCZ_filter_lookup(var,H5Z_FILTER_DEFLATE,&tmp))) goto done;
|
|
havedeflate = (tmp == NULL ? 0 : 1);
|
|
|
|
if((stat = NCZ_filter_lookup(var,H5Z_FILTER_SZIP,&tmp))) goto done;
|
|
haveszip = (tmp == NULL ? 0 : 1);
|
|
|
|
/* If incoming filter not already defined, then check for conflicts */
|
|
if(oldspec == NULL) {
|
|
if(id == H5Z_FILTER_DEFLATE) {
|
|
int level;
|
|
if(nparams != 1)
|
|
{stat = THROW(NC_EFILTER); goto done;}/* incorrect no. of parameters */
|
|
level = (int)params[0];
|
|
if (level < NC_MIN_DEFLATE_LEVEL || level > NC_MAX_DEFLATE_LEVEL)
|
|
{stat = THROW(NC_EINVAL); goto done;}
|
|
/* If szip compression is already applied, return error. */
|
|
if(haveszip) {stat = THROW(NC_EINVAL); goto done;}
|
|
}
|
|
if(id == H5Z_FILTER_SZIP) { /* Do error checking */
|
|
if(nparams != 2)
|
|
{stat = THROW(NC_EFILTER); goto done;}/* incorrect no. of parameters */
|
|
/* Pixels per block must be an even number, < 32. */
|
|
if (params[1] % 2 || params[1] > NC_MAX_PIXELS_PER_BLOCK)
|
|
{stat = THROW(NC_EINVAL); goto done;}
|
|
/* If zlib compression is already applied, return error. */
|
|
if(havedeflate) {stat = THROW(NC_EINVAL); goto done;}
|
|
}
|
|
/* Filter => chunking */
|
|
var->storage = NC_CHUNKED;
|
|
/* Determine default chunksizes for this variable unless already specified */
|
|
if(var->chunksizes && !var->chunksizes[0]) {
|
|
/* Should this throw error? */
|
|
if((stat = nc4_find_default_chunksizes2(grp, var)))
|
|
goto done;
|
|
}
|
|
}
|
|
/* More error checking */
|
|
if(id == H5Z_FILTER_SZIP) { /* szip X chunking error checking */
|
|
/* For szip, the pixels_per_block parameter must not be greater
|
|
* than the number of elements in a chunk of data. */
|
|
size_t num_elem = 1;
|
|
int d;
|
|
for (d = 0; d < var->ndims; d++)
|
|
if (var->dim[d]->len)
|
|
num_elem *= var->dim[d]->len;
|
|
/* Pixels per block must be <= number of elements. */
|
|
if (params[1] > num_elem)
|
|
{stat = THROW(NC_EINVAL); goto done;}
|
|
}
|
|
/* addfilter can handle case where filter is already defined, and will just replace parameters */
|
|
if((stat = NCZ_addfilter(h5,var,id,nparams,params)))
|
|
goto done;
|
|
if (h5->parallel)
|
|
{stat = THROW(NC_EINVAL); goto done;}
|
|
|
|
done:
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
int
|
|
NCZ_inq_var_filter_ids(int ncid, int varid, size_t* nfiltersp, unsigned int* ids)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NC *nc;
|
|
NC_FILE_INFO_T* h5 = NULL;
|
|
NC_GRP_INFO_T* grp = NULL;
|
|
NC_VAR_INFO_T* var = NULL;
|
|
NClist* flist = NULL;
|
|
size_t nfilters = 0;
|
|
|
|
ZTRACE(1,"ncid=%d varid=%d",ncid,varid);
|
|
|
|
if((stat = NC_check_id(ncid,&nc))) goto done;
|
|
assert(nc);
|
|
|
|
/* Find info for this file and group and var, and set pointer to each. */
|
|
if ((stat = ncz_find_grp_file_var(ncid, varid, &h5, &grp, &var)))
|
|
{stat = THROW(stat); goto done;}
|
|
|
|
assert(h5 && var && var->hdr.id == varid);
|
|
|
|
/* Make sure all the filters are defined */
|
|
if((stat = NCZ_filter_initialize())) goto done;
|
|
|
|
flist = var->filters;
|
|
|
|
nfilters = nclistlength(flist);
|
|
if(nfilters > 0 && ids != NULL) {
|
|
int k;
|
|
for(k=0;k<nfilters;k++) {
|
|
struct NCZ_Filter* f = (struct NCZ_Filter*)nclistget(flist,k);
|
|
ids[k] = f->hdf5.id;
|
|
}
|
|
}
|
|
if(nfiltersp) *nfiltersp = nfilters;
|
|
|
|
done:
|
|
return ZUNTRACEX(stat, "nfilters=%u", nfilters);
|
|
}
|
|
|
|
int
|
|
NCZ_inq_var_filter_info(int ncid, int varid, unsigned int id, size_t* nparamsp, unsigned int* params)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NC *nc;
|
|
NC_FILE_INFO_T* h5 = NULL;
|
|
NC_GRP_INFO_T* grp = NULL;
|
|
NC_VAR_INFO_T* var = NULL;
|
|
struct NCZ_Filter* spec = NULL;
|
|
|
|
ZTRACE(1,"ncid=%d varid=%d id=%u",ncid,varid,id);
|
|
|
|
if((stat = NC_check_id(ncid,&nc))) goto done;
|
|
assert(nc);
|
|
|
|
/* Find info for this file and group and var, and set pointer to each. */
|
|
if ((stat = ncz_find_grp_file_var(ncid, varid, &h5, &grp, &var)))
|
|
{stat = THROW(stat); goto done;}
|
|
|
|
assert(h5 && var && var->hdr.id == varid);
|
|
|
|
/* Make sure all the plugins are defined */
|
|
if((stat = NCZ_filter_initialize())) goto done;
|
|
|
|
if((stat = NCZ_filter_lookup(var,id,&spec))) goto done;
|
|
if(spec != NULL) {
|
|
#if 0
|
|
if(spec->flags & FLAG_WORKING) {/* working params are available */
|
|
if(spec->plugin->codec.codec->NCZ_visible_parameters) {
|
|
stat = spec->plugin->codec.codec->NCZ_visible_parameters(ncid,varid,
|
|
spec->hdf5.working.nparams,spec->hdf5.working.params,
|
|
&spec->hdf5.visible.nparams,&spec->hdf5.visible.params);
|
|
#ifdef DEBUGF
|
|
fprintf(stderr,"DEBUGF: NCZ_visible_parameters: ncid=%d varid=%d working=%s visible=%s\n",ncid,varid,
|
|
printnczparams(spec->hdf5.visible),printnczparams(spec->hdf5.working));
|
|
#endif
|
|
if(stat) goto done;
|
|
}
|
|
spec->flags |= FLAG_VISIBLE;
|
|
}
|
|
#endif
|
|
/* return the current visible parameters */
|
|
if(nparamsp) *nparamsp = spec->hdf5.visible.nparams;
|
|
if(params && spec->hdf5.visible.nparams > 0)
|
|
memcpy(params,spec->hdf5.visible.params,sizeof(unsigned int)*spec->hdf5.visible.nparams);
|
|
} else {
|
|
ZLOG(NCLOGWARN,"no such filter: %u",(unsigned)id);
|
|
stat = NC_ENOFILTER;
|
|
}
|
|
done:
|
|
return ZUNTRACEX(stat,"nparams=%u",(unsigned)(nparamsp?*nparamsp:0));
|
|
}
|
|
#endif /*ENABLE_NCZARR_FILTERS*/
|
|
|
|
/**************************************************/
|
|
/* Filter application functions */
|
|
|
|
int
|
|
NCZ_filter_initialize(void)
|
|
{
|
|
int stat = NC_NOERR;
|
|
ZTRACE(6,"");
|
|
if(NCZ_filter_initialized) goto done;
|
|
{
|
|
NCZ_filter_initialized = 1;
|
|
memset(loaded_plugins,0,sizeof(loaded_plugins));
|
|
#ifdef ENABLE_NCZARR_FILTERS
|
|
if((stat = NCZ_load_all_plugins())) goto done;
|
|
#endif
|
|
}
|
|
done:
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
int
|
|
NCZ_filter_finalize(void)
|
|
{
|
|
int stat = NC_NOERR;
|
|
int i;
|
|
ZTRACE(6,"");
|
|
if(!NCZ_filter_initialized) goto done;
|
|
#ifdef ENABLE_NCZARR_FILTERS
|
|
/* Reclaim all loaded filters */
|
|
for(i=0;i<=loaded_plugins_max;i++) {
|
|
NCZ_unload_plugin(loaded_plugins[i]);
|
|
loaded_plugins[i] = NULL;
|
|
}
|
|
/* Reclaim the defaults library; Must occur as last act */
|
|
if(default_lib != NULL) {(void)ncpsharedlibfree(default_lib); default_lib = NULL; codec_defaults = NULL;}
|
|
#else
|
|
memset(loaded_plugins,0,sizeof(loaded_plugins));
|
|
#endif
|
|
done:
|
|
NCZ_filter_initialized = 0;
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
static int
|
|
NCZ_plugin_save(int filterid, NCZ_Plugin* p)
|
|
{
|
|
int stat = NC_NOERR;
|
|
ZTRACE(6,"filterid=%d p=%p",filterid,p);
|
|
if(filterid <= 0 || filterid >= H5Z_FILTER_MAX)
|
|
{stat = NC_EINVAL; goto done;}
|
|
if(filterid > loaded_plugins_max) loaded_plugins_max = filterid;
|
|
loaded_plugins[filterid] = p;
|
|
done:
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
static int
|
|
NCZ_plugin_loaded(int filterid, NCZ_Plugin** pp)
|
|
{
|
|
int stat = NC_NOERR;
|
|
struct NCZ_Plugin* plug = NULL;
|
|
ZTRACE(6,"filterid=%d",filterid);
|
|
if(filterid <= 0 || filterid >= H5Z_FILTER_MAX)
|
|
{stat = NC_EINVAL; goto done;}
|
|
if(filterid <= loaded_plugins_max)
|
|
plug = loaded_plugins[filterid];
|
|
if(pp) *pp = plug;
|
|
done:
|
|
return ZUNTRACEX(stat,"plugin=%p",*pp);
|
|
}
|
|
|
|
int
|
|
NCZ_applyfilterchain(const NC_FILE_INFO_T* file, NC_VAR_INFO_T* var, NClist* chain, size_t inlen, void* indata, size_t* outlenp, void** outdatap, int encode)
|
|
{
|
|
int i, stat = NC_NOERR;
|
|
void* lastbuffer = NULL; /* if not null, then last allocated buffer */
|
|
|
|
ZTRACE(6,"|chain|=%u inlen=%u indata=%p encode=%d", (unsigned)nclistlength(chain), (unsigned)inlen, indata, encode);
|
|
|
|
/* Make sure all the filters are loaded && setup */
|
|
for(i=0;i<nclistlength(chain);i++) {
|
|
struct NCZ_Filter* f = (struct NCZ_Filter*)nclistget(chain,i);
|
|
assert(f != NULL && f->hdf5.id > 0 && f->plugin != NULL);
|
|
if(!(f->flags & FLAG_WORKING)) {/* working not yet available */
|
|
if((stat = ensure_working(var,f))) goto done;
|
|
}
|
|
}
|
|
|
|
{
|
|
struct NCZ_Filter* f = NULL;
|
|
const H5Z_class2_t* ff = NULL;
|
|
size_t current_alloc = inlen;
|
|
void* current_buf = indata;
|
|
size_t current_used = inlen;
|
|
size_t next_alloc = 0;
|
|
void* next_buf = NULL;
|
|
size_t next_used = 0;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr,"current: alloc=%u used=%u buf=%p\n",(unsigned)current_alloc,(unsigned)current_used,current_buf);
|
|
#endif
|
|
/* Apply in proper order */
|
|
if(encode) {
|
|
for(i=0;i<nclistlength(chain);i++) {
|
|
f = (struct NCZ_Filter*)nclistget(chain,i);
|
|
ff = f->plugin->hdf5.filter;
|
|
/* code can be simplified */
|
|
next_alloc = current_alloc;
|
|
next_buf = current_buf;
|
|
next_used = 0;
|
|
next_used = ff->filter(0,f->hdf5.working.nparams,f->hdf5.working.params,current_used,&next_alloc,&next_buf);
|
|
#ifdef DEBUG
|
|
fprintf(stderr,"next: alloc=%u used=%u buf=%p\n",(unsigned)next_alloc,(unsigned)next_used,next_buf);
|
|
#endif
|
|
if(next_used == 0) {stat = NC_EFILTER; lastbuffer = next_buf; goto done; }
|
|
/* If the filter did not need to create a new buffer, then next == current else current was reclaimed */
|
|
current_buf = next_buf;
|
|
current_alloc = next_alloc;
|
|
current_used = next_used;
|
|
}
|
|
} else {
|
|
/* Apply in reverse order */
|
|
for(i=nclistlength(chain)-1;i>=0;i--) {
|
|
f = (struct NCZ_Filter*)nclistget(chain,i);
|
|
ff = f->plugin->hdf5.filter;
|
|
/* code can be simplified */
|
|
next_alloc = current_alloc;
|
|
next_buf = current_buf;
|
|
next_used = 0;
|
|
next_used = ff->filter(H5Z_FLAG_REVERSE,f->hdf5.working.nparams,f->hdf5.working.params,current_used,&next_alloc,&next_buf);
|
|
#ifdef DEBUG
|
|
fprintf(stderr,"next: alloc=%u used=%u buf=%p\n",(unsigned)next_alloc,(unsigned)next_used,next_buf);
|
|
#endif
|
|
if(next_used == 0) {stat = NC_EFILTER; lastbuffer = next_buf; goto done;}
|
|
/* If the filter did not need to create a new buffer, then next == current else current was reclaimed */
|
|
current_buf = next_buf;
|
|
current_alloc = next_alloc;
|
|
current_used = next_used;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
fprintf(stderr,"current: alloc=%u used=%u buf=%p\n",(unsigned)current_alloc,(unsigned)current_used,current_buf);
|
|
#endif
|
|
/* return results */
|
|
if(outlenp) {*outlenp = current_used;} /* or should it be current_alloc? */
|
|
if(outdatap) {*outdatap = current_buf;}
|
|
}
|
|
|
|
done:
|
|
if(lastbuffer != NULL && lastbuffer != indata) nullfree(lastbuffer); /* cleanup */
|
|
return ZUNTRACEX(stat,"outlen=%u outdata=%p",(unsigned)*outlenp,*outdatap);
|
|
}
|
|
|
|
/**************************************************/
|
|
/* JSON Parse/unparse of filters */
|
|
int
|
|
NCZ_filter_jsonize(const NC_FILE_INFO_T* file, const NC_VAR_INFO_T* var, NCZ_Filter* filter, NCjson** jfilterp)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NCjson* jfilter = NULL;
|
|
|
|
ZTRACE(6,"var=%s filter=%s",var->hdr.name,(filter != NULL && filter->codec.id != NULL?filter->codec.id:"null"));
|
|
|
|
/* assumptions */
|
|
assert(filter->flags & FLAG_WORKING);
|
|
|
|
/* Convert the HDF5 id + parameters to the codec form */
|
|
|
|
/* We need to ensure the the current visible parameters are defined and had the opportunity to come
|
|
from the working parameters */
|
|
assert((filter->flags & (FLAG_VISIBLE | FLAG_WORKING)) == (FLAG_VISIBLE | FLAG_WORKING));
|
|
#if 0
|
|
if((stat = rebuild_visible(var,filter))) goto done;
|
|
#endif
|
|
|
|
/* Convert the visible parameters back to codec */
|
|
/* Clear any previous codec */
|
|
nullfree(filter->codec.id); filter->codec.id = NULL;
|
|
nullfree(filter->codec.codec); filter->codec.codec = NULL;
|
|
filter->codec.id = strdup(filter->plugin->codec.codec->codecid);
|
|
if(filter->plugin->codec.codec->NCZ_hdf5_to_codec) {
|
|
stat = filter->plugin->codec.codec->NCZ_hdf5_to_codec(filter->hdf5.visible.nparams,filter->hdf5.visible.params,&filter->codec.codec);
|
|
#ifdef DEBUGF
|
|
fprintf(stderr,"DEBUGF: NCZ_hdf5_to_codec: visible=%s codec=%s\n",printnczparams(filter->hdf5.visible),filter->codec.codec);
|
|
#endif
|
|
if(stat) goto done;
|
|
} else
|
|
{stat = NC_EFILTER; goto done;}
|
|
|
|
/* Parse the codec as the return */
|
|
if(NCJparse(filter->codec.codec,0,&jfilter) < 0) {stat = NC_EFILTER; goto done;}
|
|
if(jfilterp) {*jfilterp = jfilter; jfilter = NULL;}
|
|
|
|
done:
|
|
NCJreclaim(jfilter);
|
|
return ZUNTRACEX(stat,"codec=%s",NULLIFY(filter->codec.codec));
|
|
}
|
|
|
|
|
|
/* Build filter from parsed Zarr metadata */
|
|
int
|
|
NCZ_filter_build(const NC_FILE_INFO_T* file, NC_VAR_INFO_T* var, const NCjson* jfilter)
|
|
{
|
|
int i,stat = NC_NOERR;
|
|
NCZ_Filter* filter = NULL;
|
|
NCjson* jvalue = NULL;
|
|
NCZ_Plugin* plugin = NULL;
|
|
NCZ_Codec codec = codec_empty;
|
|
NCZ_HDF5 hdf5 = hdf5_empty;
|
|
|
|
ZTRACE(6,"file=%s var=%s jfilter=%s",file->hdr.name,var->hdr.name,NCJtrace(jfilter));
|
|
|
|
if(var->filters == NULL) var->filters = nclistnew();
|
|
|
|
/* Get the id of this codec filter */
|
|
if(NCJdictget(jfilter,"id",&jvalue)<0) {stat = NC_EFILTER; goto done;}
|
|
if(NCJsort(jvalue) != NCJ_STRING) {
|
|
ZLOG(NCLOGERR,"no such filter: %s",NCJstring(jvalue));
|
|
stat = NC_ENOFILTER; goto done;
|
|
}
|
|
|
|
/* Build the codec */
|
|
if((codec.id = strdup(NCJstring(jvalue)))==NULL)
|
|
{stat = NC_ENOMEM; goto done;}
|
|
if(NCJunparse(jfilter,0,&codec.codec)<0) {stat = NC_EFILTER; goto done;}
|
|
|
|
/* Find the plugin for this filter */
|
|
for(i=0;i<=loaded_plugins_max;i++) {
|
|
if (!loaded_plugins[i]) continue;
|
|
if(strcmp(NCJstring(jvalue), loaded_plugins[i]->codec.codec->codecid) == 0)
|
|
{plugin = loaded_plugins[i]; break;}
|
|
}
|
|
|
|
if(plugin != NULL) {
|
|
/* Save the hdf5 id */
|
|
hdf5.id = plugin->codec.codec->hdf5id;
|
|
/* Convert the codec to hdf5 form visible parameters */
|
|
if(plugin->codec.codec->NCZ_codec_to_hdf5) {
|
|
stat = plugin->codec.codec->NCZ_codec_to_hdf5(codec.codec,&hdf5.visible.nparams,&hdf5.visible.params);
|
|
#ifdef DEBUGF
|
|
fprintf(stderr,"DEBUGF: NCZ_codec_to_hdf5: codec=%s, hdf5=%s\n",printcodec(codec),printhdf5(hdf5));
|
|
#endif
|
|
if(stat) goto done;
|
|
}
|
|
|
|
if((filter = calloc(1,sizeof(NCZ_Filter)))==NULL) {stat = NC_ENOMEM; goto done;}
|
|
filter->flags |= FLAG_VISIBLE;
|
|
filter->hdf5 = hdf5; hdf5 = hdf5_empty;
|
|
filter->codec = codec; codec = codec_empty;
|
|
filter->flags |= FLAG_CODEC;
|
|
filter->plugin = plugin; plugin = NULL;
|
|
}
|
|
|
|
if(filter != NULL) {
|
|
NClist* filterlist = (NClist*)var->filters;
|
|
nclistpush(filterlist,filter);
|
|
filter = NULL;
|
|
}
|
|
|
|
done:
|
|
ncz_hdf5_clear(&hdf5);
|
|
ncz_codec_clear(&codec);
|
|
NCZ_filter_free(filter);
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
/**************************************************/
|
|
/* Filter loading */
|
|
|
|
/*
|
|
Get entries in a path that is assumed to be a directory.
|
|
*/
|
|
|
|
#ifdef _WIN32
|
|
|
|
static int
|
|
getentries(const char* path, NClist* contents)
|
|
{
|
|
/* Iterate over the entries in the directory */
|
|
int ret = NC_NOERR;
|
|
errno = 0;
|
|
WIN32_FIND_DATA FindFileData;
|
|
HANDLE dir = NULL;
|
|
char* ffpath = NULL;
|
|
char* lpath = NULL;
|
|
size_t len;
|
|
char* d = NULL;
|
|
|
|
ZTRACE(6,"path=%s",path);
|
|
|
|
/* We need to process the path to make it work with FindFirstFile */
|
|
len = strlen(path);
|
|
/* Need to terminate path with '/''*' */
|
|
ffpath = (char*)malloc(len+2+1);
|
|
memcpy(ffpath,path,len);
|
|
if(path[len-1] != '/') {
|
|
ffpath[len] = '/';
|
|
len++;
|
|
}
|
|
ffpath[len] = '*'; len++;
|
|
ffpath[len] = '\0';
|
|
|
|
/* localize it */
|
|
if((ret = nczm_localize(ffpath,&lpath,LOCALIZE))) goto done;
|
|
dir = FindFirstFile(lpath, &FindFileData);
|
|
if(dir == INVALID_HANDLE_VALUE) {
|
|
/* Distinquish not-a-directory from no-matching-file */
|
|
switch (GetLastError()) {
|
|
case ERROR_FILE_NOT_FOUND: /* No matching files */ /* fall thru */
|
|
ret = NC_NOERR;
|
|
goto done;
|
|
case ERROR_DIRECTORY: /* not a directory */
|
|
default:
|
|
ret = NC_EEMPTY;
|
|
goto done;
|
|
}
|
|
}
|
|
do {
|
|
char* p = NULL;
|
|
const char* name = NULL;
|
|
name = FindFileData.cFileName;
|
|
if(strcmp(name,".")==0 || strcmp(name,"..")==0)
|
|
continue;
|
|
nclistpush(contents,strdup(name));
|
|
} while(FindNextFile(dir, &FindFileData));
|
|
|
|
done:
|
|
if(dir) FindClose(dir);
|
|
nullfree(lpath);
|
|
nullfree(ffpath);
|
|
nullfree(d);
|
|
errno = 0;
|
|
return ZUNTRACEX(ret,"|contents|=%d",(int)nclistlength(contents));
|
|
}
|
|
|
|
#else /* !_WIN32 */
|
|
|
|
int
|
|
getentries(const char* path, NClist* contents)
|
|
{
|
|
int ret = NC_NOERR;
|
|
errno = 0;
|
|
DIR* dir = NULL;
|
|
|
|
ZTRACE(6,"path=%s",path);
|
|
|
|
dir = NCopendir(path);
|
|
if(dir == NULL)
|
|
{ret = (errno); goto done;}
|
|
for(;;) {
|
|
const char* name = NULL;
|
|
struct dirent* de = NULL;
|
|
errno = 0;
|
|
de = readdir(dir);
|
|
if(de == NULL)
|
|
{ret = (errno); goto done;}
|
|
if(strcmp(de->d_name,".")==0 || strcmp(de->d_name,"..")==0)
|
|
continue;
|
|
name = de->d_name;
|
|
nclistpush(contents,strdup(name));
|
|
}
|
|
done:
|
|
if(dir) NCclosedir(dir);
|
|
errno = 0;
|
|
return ZUNTRACEX(ret,"|contents|=%d",(int)nclistlength(contents));
|
|
}
|
|
#endif /*_WIN32*/
|
|
|
|
static int
|
|
NCZ_load_all_plugins(void)
|
|
{
|
|
int i,ret = NC_NOERR;
|
|
const char* pluginroot = NULL;
|
|
struct stat buf;
|
|
NClist* dirs = nclistnew();
|
|
#ifdef _WIN32
|
|
char pluginpath32[4096];
|
|
#endif
|
|
|
|
ZTRACE(6,"");
|
|
|
|
#ifdef DEBUGL
|
|
fprintf(stderr,"DEBUGL: NCZ_load_all_plugins\n");
|
|
#endif
|
|
|
|
/* Find the plugin directory root(s) */
|
|
pluginroot = getenv(plugin_env);
|
|
if(pluginroot == NULL || strlen(pluginroot) == 0) {
|
|
#ifdef _WIN32
|
|
const char* win32_root;
|
|
win32_root = getenv(win32_root_env);
|
|
if(win32_root != NULL && strlen(win32_root) > 0) {
|
|
snprintf(pluginpath32,sizeof(pluginpath32),plugin_dir_win,win32_root);
|
|
pluginroot = pluginpath32;
|
|
} else
|
|
pluginroot = NULL;
|
|
#else /*!_WIN32*/
|
|
pluginroot = plugin_dir_unix;
|
|
#endif
|
|
}
|
|
|
|
ZTRACEMORE(6,"pluginroot=%s",(pluginroot?pluginroot:"null"));
|
|
if(pluginroot == NULL) {
|
|
ZLOG(NCLOGERR,"no pluginroot: %s",plugin_env);
|
|
ret = NC_ENOFILTER; goto done;
|
|
}
|
|
|
|
if((ret = NCZ_split_plugin_path(pluginroot,dirs))) goto done;
|
|
|
|
for(i=0;i<nclistlength(dirs);i++) {
|
|
const char* dir = (const char*)nclistget(dirs,i);
|
|
/* Make sure the root is actually a directory */
|
|
errno = 0;
|
|
ret = NCstat(dir, &buf);
|
|
#if 1
|
|
ZTRACEMORE(6,"stat: ret=%d, errno=%d st_mode=%d",ret,errno,buf.st_mode);
|
|
#endif
|
|
if(ret < 0) {
|
|
ret = (errno);
|
|
} else if(! S_ISDIR(buf.st_mode))
|
|
ret = NC_EINVAL;
|
|
if(ret) goto done;
|
|
|
|
/* Try to load plugins from this directory */
|
|
if((ret = NCZ_load_plugin_dir(dir))) goto done;
|
|
}
|
|
|
|
if(codec_defaults != NULL) { /* Try to provide default for any HDF5 filters without matching Codec. */
|
|
NCZ_codec_t** dfalts = NULL;
|
|
/* Search the defaults */
|
|
for(dfalts = codec_defaults;*dfalts;dfalts++) {
|
|
NCZ_codec_t* codec = *dfalts;
|
|
int hdf5id = codec->hdf5id;
|
|
NCZ_Plugin* p = loaded_plugins[hdf5id]; /* get candidate */
|
|
if(p != NULL && p->hdf5.filter != NULL && p->codec.codec == NULL) {
|
|
#ifdef DEBUGL
|
|
fprintf(stderr,"DEBUGL: plugin defaulted: id=%u, codec=%s\n",hdf5id,codec->codecid);
|
|
#endif
|
|
p->codec.codec = codec; codec = NULL;
|
|
p->codec.codeclib = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Expunge all plugins for which we do not have both HDF5 and codec */
|
|
{
|
|
int i;
|
|
NCZ_Plugin* p;
|
|
for(i=0;i<loaded_plugins_max;i++) {
|
|
if((p = loaded_plugins[i]) != NULL) {
|
|
if(p->hdf5.filter == NULL || p->codec.codec == NULL) {
|
|
#ifdef DEBUGL
|
|
fprintf(stderr,"DEBUGL: plugin expunged: id=%u\n",p->hdf5.filter->id);
|
|
#endif
|
|
/* expunge this entry */
|
|
(void)NCZ_unload_plugin(p);
|
|
loaded_plugins[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Iniitalize all remaining plugins */
|
|
{
|
|
int i;
|
|
NCZ_Plugin* p;
|
|
for(i=0;i<loaded_plugins_max;i++) {
|
|
if((p = loaded_plugins[i]) != NULL) {
|
|
if(p->hdf5.filter != NULL && p->codec.codec != NULL) {
|
|
if(p->codec.codec && p->codec.codec->NCZ_codec_initialize)
|
|
p->codec.codec->NCZ_codec_initialize();
|
|
#ifdef DEBUGL
|
|
fprintf(stderr,"DEBUGL: plugin initialized: id=%u\n",p->hdf5.filter->id);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
nclistfreeall(dirs);
|
|
errno = 0;
|
|
return ZUNTRACE(ret);
|
|
}
|
|
|
|
static int
|
|
NCZ_split_plugin_path(const char* path0, NClist* list)
|
|
{
|
|
int i,stat = NC_NOERR;
|
|
char* path = NULL;
|
|
char* p;
|
|
int count;
|
|
size_t plen;
|
|
#ifdef _WIN32
|
|
const char* seps = ";";
|
|
#else
|
|
const char* seps = ";:";
|
|
#endif
|
|
|
|
if(path0 == NULL || path0[0] == '\0') goto done;
|
|
plen = strlen(path0);
|
|
if((path = malloc(plen+1+1))==NULL) {stat = NC_ENOMEM; goto done;}
|
|
memcpy(path,path0,plen);
|
|
path[plen] = '\0'; path[plen+1] = '\0'; /* double null term */
|
|
for(count=0,p=path;*p;p++) {
|
|
if(strchr(seps,*p) != NULL) {*p = '\0'; count++;}
|
|
}
|
|
count++; /* for last piece */
|
|
for(p=path,i=0;i<count;i++) {
|
|
size_t len = strlen(p);
|
|
if(len > 0)
|
|
nclistpush(list,strdup(p));
|
|
p = p+len+1; /* point to next piece */
|
|
}
|
|
|
|
done:
|
|
nullfree(path);
|
|
return stat;
|
|
}
|
|
|
|
|
|
/* Load all the filters within a specified directory */
|
|
static int
|
|
NCZ_load_plugin_dir(const char* path)
|
|
{
|
|
int i,stat = NC_NOERR;
|
|
size_t pathlen;
|
|
NClist* contents = nclistnew();
|
|
char* file = NULL;
|
|
|
|
ZTRACE(7,"path=%s",path);
|
|
|
|
#ifdef DEBUGL
|
|
fprintf(stderr,"DEBUGL: NCZ_load_plugin_dir: path=%s\n",path);
|
|
#endif
|
|
|
|
if(path == NULL) {stat = NC_EINVAL; goto done;}
|
|
pathlen = strlen(path);
|
|
if(pathlen == 0) {stat = NC_EINVAL; goto done;}
|
|
|
|
if((stat = getentries(path,contents))) goto done;
|
|
for(i=0;i<nclistlength(contents);i++) {
|
|
const char* name = (const char*)nclistget(contents,i);
|
|
size_t nmlen = strlen(name);
|
|
size_t flen = pathlen+1+nmlen+1;
|
|
int id;
|
|
NCZ_Plugin* plugin = NULL;
|
|
|
|
assert(nmlen > 0);
|
|
nullfree(file); file = NULL;
|
|
if((file = (char*)malloc(flen))==NULL) {stat = NC_ENOMEM; goto done;}
|
|
file[0] = '\0';
|
|
strlcat(file,path,flen);
|
|
strlcat(file,"/",flen);
|
|
strlcat(file,name,flen);
|
|
/* See if can load the file */
|
|
switch ((stat = NCZ_load_plugin(file,&plugin))) {
|
|
case NC_NOERR: break;
|
|
case NC_ENOFILTER: case NC_ENOTFOUND: stat = NC_NOERR; break; /* will cause it to be ignored */
|
|
default: goto done;
|
|
}
|
|
if(plugin != NULL) {
|
|
id = plugin->hdf5.filter->id;
|
|
if(loaded_plugins[id] == NULL) {
|
|
loaded_plugins[id] = plugin;
|
|
if(id > loaded_plugins_max) loaded_plugins_max = id;
|
|
#ifdef DEBUGL
|
|
fprintf(stderr,"DEBUGL: plugin loaded: %s\n",printplugin(plugin));
|
|
#endif
|
|
} else {
|
|
#ifdef DEBUGL
|
|
fprintf(stderr,"DEBUGL: plugin duplicate: %s\n",printplugin(plugin));
|
|
#endif
|
|
NCZ_unload_plugin(plugin); /* its a duplicate */
|
|
}
|
|
} else
|
|
stat = NC_NOERR; /*ignore failure */
|
|
}
|
|
|
|
done:
|
|
nullfree(file);
|
|
nclistfreeall(contents);
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
static int
|
|
NCZ_load_plugin(const char* path, struct NCZ_Plugin** plugp)
|
|
{
|
|
int stat = NC_NOERR;
|
|
NCZ_Plugin* plugin = NULL;
|
|
const H5Z_class2_t* h5class = NULL;
|
|
const NCZ_codec_t* codec = NULL;
|
|
NCPSharedLib* lib = NULL;
|
|
int flags = NCP_GLOBAL;
|
|
int h5id = -1;
|
|
|
|
assert(path != NULL && strlen(path) > 0 && plugp != NULL);
|
|
|
|
ZTRACE(8,"path=%s",path);
|
|
|
|
#ifdef DEBUGL
|
|
fprintf(stderr,"DEBUGL: NCZ_load_plugin: path=%s\n",path);
|
|
#endif
|
|
|
|
if(plugp) *plugp = NULL;
|
|
|
|
#ifdef _WIN32
|
|
/*triage because visual studio does a popup if the file will not load*/
|
|
if(memcmp(path+(strlen(path)-4),".dll",4) != 0) {
|
|
stat = NC_ENOFILTER; goto done;
|
|
}
|
|
#endif
|
|
|
|
/* load the shared library */
|
|
if((stat = ncpsharedlibnew(&lib))) goto done;
|
|
if((stat = ncpload(lib,path,flags))) goto done;
|
|
|
|
/* See what we have */
|
|
{
|
|
H5PL_get_plugin_type_proto gpt = (H5PL_get_plugin_type_proto)ncpgetsymbol(lib,"H5PLget_plugin_type");
|
|
H5PL_get_plugin_info_proto gpi = (H5PL_get_plugin_info_proto)ncpgetsymbol(lib,"H5PLget_plugin_info");
|
|
NCZ_get_codec_info_proto npi = (NCZ_get_codec_info_proto)ncpgetsymbol(lib,"NCZ_get_codec_info");
|
|
NCZ_codec_info_defaults_proto cpd = (NCZ_codec_info_defaults_proto)ncpgetsymbol(lib,"NCZ_codec_info_defaults");
|
|
|
|
/* Deal with defaults first */
|
|
if(cpd != NULL) {
|
|
if(codec_defaults == NULL) {
|
|
codec_defaults = (NCZ_codec_t**)cpd();
|
|
default_lib = lib; lib = NULL;
|
|
}
|
|
goto done;
|
|
}
|
|
|
|
if(gpt != NULL && gpi != NULL) {
|
|
/* get HDF5 info */
|
|
H5PL_type_t h5type = gpt();
|
|
h5class = gpi();
|
|
/* Verify */
|
|
if(h5type != H5PL_TYPE_FILTER) {stat = NC_EPLUGIN; goto done;}
|
|
if(h5class->version != H5Z_CLASS_T_VERS) {stat = NC_EFILTER; goto done;}
|
|
}
|
|
|
|
if(npi != NULL) {
|
|
/* get Codec info */
|
|
codec = npi();
|
|
/* Verify */
|
|
if(codec->version != NCZ_CODEC_CLASS_VER) {stat = NC_EPLUGIN; goto done;}
|
|
if(codec->sort != NCZ_CODEC_HDF5) {stat = NC_EPLUGIN; goto done;}
|
|
}
|
|
}
|
|
|
|
/* Ignore this library if neither h5class nor codec are defined */
|
|
if(h5class == NULL && codec == NULL) {stat = NC_ENOFILTER; goto done;}
|
|
|
|
#ifdef DEBUGL
|
|
fprintf(stderr,"DEBUGL: load: %s:",path);
|
|
if(h5class) fprintf(stderr," %u",(unsigned)h5class->id);
|
|
if(codec) fprintf(stderr," %u/%s",codec->hdf5id,codec->codecid);
|
|
fprintf(stderr,"\n");
|
|
#endif
|
|
|
|
if(h5class != NULL && codec != NULL) {
|
|
/* Verify consistency of the HDF5 and the Codec */
|
|
if(h5class->id != codec->hdf5id) goto done; /* ignore */
|
|
}
|
|
|
|
/* There are several cases to consider:
|
|
1. This library has both HDF5 API and Codec API => merge
|
|
2. This library has HDF5 API only and Codec API was already found in another library => merge
|
|
3. This library has Codec API only and HDF5 API was already found in another library => merge
|
|
*/
|
|
|
|
/* Get any previous plugin entry for this id; may be NULL */
|
|
if(h5class != NULL) {
|
|
h5id = h5class->id;
|
|
if((stat = NCZ_plugin_loaded(h5class->id,&plugin))) goto done;
|
|
} else if(codec != NULL) {
|
|
h5id = codec->hdf5id;
|
|
if((stat = NCZ_plugin_loaded(codec->hdf5id,&plugin))) goto done;
|
|
}
|
|
if(plugin == NULL) {
|
|
/* create new entry */
|
|
if((plugin = (NCZ_Plugin*)calloc(1,sizeof(NCZ_Plugin)))==NULL) {stat = NC_ENOMEM; goto done;}
|
|
}
|
|
|
|
/* Fill in the plugin */
|
|
if(plugin->hdf5.filter == NULL) {
|
|
plugin->hdf5.filter = h5class;
|
|
plugin->hdf5.hdf5lib = lib;
|
|
lib = NULL;
|
|
}
|
|
if(plugin->codec.codec == NULL) {
|
|
plugin->codec.codec = codec;
|
|
plugin->codec.codeclib = lib;
|
|
lib = NULL;
|
|
}
|
|
|
|
#ifdef DEBUGL
|
|
if(plugin)
|
|
fprintf(stderr,"DEBUGL: load_plugin: %s\n",printplugin(plugin));
|
|
#endif
|
|
|
|
|
|
/* Cleanup */
|
|
if(plugin->hdf5.hdf5lib == plugin->codec.codeclib)
|
|
plugin->codec.codeclib = NULL;
|
|
if((stat=NCZ_plugin_save(h5id,plugin))) goto done;
|
|
plugin = NULL;
|
|
|
|
done:
|
|
if(lib) {
|
|
(void)ncpsharedlibfree(lib);
|
|
}
|
|
if(plugin) NCZ_unload_plugin(plugin);
|
|
return ZUNTRACEX(stat,"plug=%p",*plugp);
|
|
}
|
|
|
|
static int
|
|
NCZ_unload_plugin(NCZ_Plugin* plugin)
|
|
{
|
|
ZTRACE(9,"plugin=%p",plugin);
|
|
|
|
if(plugin) {
|
|
#ifdef DEBUGL
|
|
fprintf(stderr,"DEBUGL: unload: %s\n",printplugin(plugin));
|
|
#endif
|
|
if(plugin->codec.codec && plugin->codec.codec->NCZ_codec_finalize)
|
|
plugin->codec.codec->NCZ_codec_finalize();
|
|
if(plugin->hdf5.filter != NULL) loaded_plugins[plugin->hdf5.filter->id] = NULL;
|
|
if(plugin->hdf5.hdf5lib != NULL) (void)ncpsharedlibfree(plugin->hdf5.hdf5lib);
|
|
if(plugin->codec.codeclib != NULL) (void)ncpsharedlibfree(plugin->codec.codeclib);
|
|
memset(plugin,0,sizeof(NCZ_Plugin));
|
|
free(plugin);
|
|
}
|
|
return ZUNTRACE(NC_NOERR);
|
|
}
|
|
|
|
/**************************************************/
|
|
/* _Codecs attribute */
|
|
|
|
int
|
|
NCZ_codec_attr(const NC_VAR_INFO_T* var, size_t* lenp, void* data)
|
|
{
|
|
int i,stat = NC_NOERR;
|
|
size_t len;
|
|
char* contents = NULL;
|
|
NCbytes* buf = NULL;
|
|
NClist* filters = (NClist*)var->filters;
|
|
|
|
ZTRACE(6,"var=%s",var->hdr.name);
|
|
if(nclistlength(filters) == 0) {stat = NC_ENOTATT; goto done;}
|
|
buf = ncbytesnew(); ncbytessetalloc(buf,1024);
|
|
ncbytescat(buf,"[");
|
|
for(i=0;i<nclistlength(filters);i++) {
|
|
NCZ_Filter* spec = nclistget(filters,i);
|
|
if(i > 0) ncbytescat(buf,",");
|
|
ncbytescat(buf,spec->codec.codec);
|
|
}
|
|
ncbytescat(buf,"]");
|
|
len = ncbyteslength(buf);
|
|
contents = nclistcontents(buf);
|
|
if(lenp) *lenp = len;
|
|
if(data) strncpy((char*)data,contents,len+1);
|
|
done:
|
|
ncbytesfree(buf);
|
|
return ZUNTRACEX(stat,"len=%u data=%p",(unsigned)len,data);
|
|
}
|
|
|
|
static int
|
|
ensure_working(const NC_VAR_INFO_T* var, NCZ_Filter* filter)
|
|
{
|
|
int stat = NC_NOERR;
|
|
if(!(filter->flags & FLAG_WORKING)) {
|
|
const size_t oldnparams = filter->hdf5.visible.nparams;
|
|
const unsigned* oldparams = filter->hdf5.visible.params;
|
|
|
|
assert(filter->flags & FLAG_VISIBLE);
|
|
/* Convert the visible parameters to working parameters; may also modify the visible params */
|
|
if(filter->plugin && filter->plugin->codec.codec->NCZ_modify_parameters) {
|
|
stat = filter->plugin->codec.codec->NCZ_modify_parameters(ncidfor(var),var->hdr.id,
|
|
&filter->hdf5.visible.nparams, &filter->hdf5.visible.params,
|
|
&filter->hdf5.working.nparams, &filter->hdf5.working.params);
|
|
#ifdef DEBUGF
|
|
fprintf(stderr,"DEBUGF: NCZ_modify_parameters: ncid=%d varid=%d filter=%s\n", (int)ncidfor(var),(int)var->hdr.id,
|
|
printfilter(filter));
|
|
#endif
|
|
if(stat) goto done;
|
|
/* See if the visible parameters were changed */
|
|
if(oldnparams != filter->hdf5.visible.nparams || oldparams != filter->hdf5.visible.params)
|
|
filter->flags |= FLAG_NEWVISIBLE;
|
|
} else {
|
|
/* assume visible are unchanged */
|
|
assert(oldnparams == filter->hdf5.visible.nparams && oldparams == filter->hdf5.visible.params); /* unchanged */
|
|
/* Just copy the visible parameters */
|
|
nullfree(filter->hdf5.working.params);
|
|
if((stat = paramnczclone(&filter->hdf5.working,&filter->hdf5.visible))) goto done;
|
|
}
|
|
filter->flags |= FLAG_WORKING;
|
|
}
|
|
#ifdef DEBUGF
|
|
fprintf(stderr,"DEBUGF: ensure_working_parameters: ncid=%lu varid=%u filter=%s\n", ncidfor(var), (unsigned)var->hdr.id,printfilter(filter));
|
|
#endif
|
|
done:
|
|
return THROW(stat);
|
|
}
|
|
|
|
#if 0
|
|
static int
|
|
rebuild_visible(const NC_VAR_INFO_T* var, NCZ_Filter* filter)
|
|
{
|
|
int stat = NC_NOERR;
|
|
int nvisible0;
|
|
unsigned* visible0 = NULL;
|
|
|
|
assert(filter->flags & FLAG_WORKING);
|
|
/* If the visible parameters are previously defined, save them */
|
|
if(filter->flags & FLAG_VISIBLE) {
|
|
nvisible0 = filter->hdf5.visible.nparams;
|
|
visible0 = filter->hdf5.visible.params;
|
|
filter->hdf5.visible.nparams = 0;
|
|
filter->hdf5.visible.params = NULL; /* temporary */
|
|
}
|
|
/* Cases to consider:
|
|
1. visible already defined && NCZ_visible_parameters defined => apply
|
|
2. visible not defined && NCZ_visible_parameters defined defined => apply
|
|
3. visible already defined && NCZ_visible_parameters not defined => keep originals
|
|
4. visible not defined && NCZ_visible_parameters not defined => use working parameters
|
|
*/
|
|
|
|
/* Cases 1 and 2 */
|
|
/* Convert the working parameters to visibleparameters, overwriting any existing visibles */
|
|
if(filter->plugin->codec.codec->NCZ_visible_parameters) {
|
|
stat = filter->plugin->codec.codec->NCZ_visible_parameters(ncidfor(var),var->hdr.id,
|
|
filter->hdf5.working.nparams, filter->hdf5.working.params,
|
|
&filter->hdf5.visible.nparams, &filter->hdf5.visible.params);
|
|
if(stat) goto done;
|
|
} else if(filter->flags & FLAG_CODEC) {/* Case 3 */
|
|
filter->hdf5.visible.nparams = nvisible0;
|
|
filter->hdf5.visible.params = visible0; visible0 = NULL;
|
|
} else {/* Case 4 */
|
|
/* Use the working parameters as the visible parameters */
|
|
filter->hdf5.visible.nparams = filter->hdf5.working.nparams;
|
|
if(filter->hdf5.working.nparams > 0) {
|
|
if((stat = paramnczclone(&filter->hdf5.visible,&filter->hdf5.working))) goto done;
|
|
}
|
|
}
|
|
filter->flags |= FLAG_VISIBLE;
|
|
#ifdef DEBUGF
|
|
fprintf(stderr,"DEBUGF: rebuild_visible_parameters: ncid=%lu varid=%u filter=%s\n", ncidfor(var), (unsigned)var->hdr.id,printfilter(filter));
|
|
#endif
|
|
done:
|
|
nullfree(visible0);
|
|
return THROW(stat);
|
|
}
|
|
#endif
|
|
|
|
/* Called by NCZ_enddef to ensure that the working parameters are defined */
|
|
int
|
|
NCZ_filter_setup(NC_VAR_INFO_T* var)
|
|
{
|
|
int i,stat = NC_NOERR;
|
|
NClist* filters = NULL;
|
|
|
|
ZTRACE(6,"var=%s",var->hdr.name);
|
|
|
|
filters = (NClist*)var->filters;
|
|
for(i=0;i<nclistlength(filters);i++) {
|
|
NCZ_Filter* filter = (NCZ_Filter*)nclistget(filters,i);
|
|
assert(filter != NULL && filter->plugin != NULL);
|
|
assert((filter->flags & FLAG_VISIBLE)); /* Assume visible params are defined */
|
|
/* verify */
|
|
assert(filter->hdf5.id > 0 && (filter->hdf5.visible.nparams == 0 || filter->hdf5.visible.params != NULL));
|
|
/* Initialize the working parameters */
|
|
if((stat = ensure_working(var,filter))) goto done;
|
|
#ifdef DEBUGF
|
|
fprintf(stderr,"DEBUGF: NCZ_filter_setup: ncid=%d varid=%d filter=%s\n", (int)ncidfor(var),(int)var->hdr.id,
|
|
printfilter(filter));
|
|
#endif
|
|
}
|
|
|
|
done:
|
|
return ZUNTRACE(stat);
|
|
}
|
|
|
|
/**************************************************/
|
|
|
|
/* Clone an hdf5 parameter set */
|
|
static int
|
|
paramclone(size_t nparams, unsigned** dstp, const unsigned* src)
|
|
{
|
|
unsigned* dst = NULL;
|
|
if(nparams > 0) {
|
|
if(src == NULL) return NC_EINVAL;
|
|
if((dst = (unsigned*)malloc(sizeof(unsigned) * nparams))==NULL)
|
|
return NC_ENOMEM;
|
|
memcpy(dst,src,sizeof(unsigned) * nparams);
|
|
}
|
|
if(dstp) *dstp = dst;
|
|
return NC_NOERR;
|
|
}
|
|
|
|
static int
|
|
paramnczclone(NCZ_Params* dst, const NCZ_Params* src)
|
|
{
|
|
assert(src != NULL && dst != NULL && dst->params == NULL);
|
|
*dst = *src;
|
|
return paramclone(src->nparams,&dst->params,src->params);
|
|
}
|