1 /*
2  * Copyright 2018, University Corporation for Atmospheric Research
3  * See netcdf/COPYRIGHT file for copying and redistribution conditions.
4  */
5 
6 #include "config.h"
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <string.h>
10 #ifdef _MSC_VER
11 #include <io.h>
12 #endif
13 
14 #include "netcdf.h"
15 #include "netcdf_filter.h"
16 #include "ncdispatch.h"
17 #include "nc4internal.h"
18 
19 #ifdef USE_HDF5
20 #include "hdf5internal.h"
21 #endif
22 
23 /*
24 Unified filter related code
25 */
26 
27 #ifndef H5Z_FILTER_SZIP
28 /** ID of HDF SZIP filter. */
29 #define H5Z_FILTER_SZIP 4
30 #endif
31 
32 #define LPAREN '('
33 #define RPAREN ')'
34 #define LBRACK '['
35 #define RBRACK ']'
36 
37 #define NUMCHAR "0123456789"
38 #define NAMECHAR1 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
39 #define NAMECHARN (NAMECHAR1 NUMCHAR "_-")
40 
41 /* Forward */
42 static int gettype(const int q0, const int q1, int* unsignedp);
43 
44 const struct LegalFormat {
45     const char* tag;
46     int format;
47 } legalformats[] = {
48     {"hdf5", NC_FILTER_FORMAT_HDF5},
49     {NULL, 0},
50 };
51 
52 const struct FilterName {
53     const char* name; /* name or alias as assigned by HDF group*/
54     unsigned int id;  /* id as assigned by HDF group*/
55 } known_filters[] = {
56 {"zip", 2}, /* Standard zlib compression */
57 {"zlib", 2}, /* alias */
58 {"deflate", 2}, /* alias */
59 {"szip", 4}, /* Standard szip compression */
60 {"bzip2", 307}, /* BZIP2 lossless compression used by PyTables */
61 {"lzf", 32000}, /* LZF lossless compression used by H5Py project */
62 {"blosc", 32001}, /* Blosc lossless compression used by PyTables */
63 {"mafisc", 32002}, /* Modified LZMA compression filter, MAFISC (Multidimensional Adaptive Filtering Improved Scientific data Compression) */
64 {"snappy", 32003}, /* Snappy lossless compression. */
65 {"lz4", 32004}, /* LZ4 fast lossless compression algorithm */
66 {"apax", 32005}, /* Samplify's APAX Numerical Encoding Technology */
67 {"cbf", 32006}, /* All imgCIF/CBF compressions and decompressions, including Canonical, Packed, Packed Vesrsion 2, Byte Offset and Nibble Offset. */
68 {"jpeg-xr", 32007}, /* Enables images to be compressed/decompressed with JPEG-XR compression */
69 {"bitshuffle", 32008}, /* Extreme version of shuffle filter that shuffles data at bit level instead of byte level. */
70 {"spdp", 32009}, /* SPDP fast lossless compression algorithm for single- and double-precision floating-point data. */
71 {"lpc-rice", 32010}, /* LPC-Rice multi-threaded lossless compression */
72 {"ccsds-123", 32011}, /* ESA CCSDS-123 multi-threaded compression filter */
73 {"jpeg-ls", 32012}, /* CharLS JPEG-LS multi-threaded compression filter */
74 {"zfp", 32013}, /* Rate, accuracy or precision bounded compression for floating-point arrays */
75 {"fpzip", 32014}, /* Fast and Efficient Lossy or Lossless Compressor for Floating-Point Data */
76 {"zstandard", 32015}, /* Real-time compression algorithm with wide range of compression / speed trade-off and fast decoder */
77 {"b3d", 32016}, /* GPU based image compression method developed for light-microscopy applications */
78 {"sz", 32017}, /* An error-bounded lossy compressor for scientific floating-point data */
79 {"fcidecomp", 32018}, /* EUMETSAT CharLS compression filter for use with netCDF */
80 {"user-defined", 32768}, /* First user-defined filter */
81 {NULL,0}
82 };
83 
84 /**************************************************/
85 /*
86 Parse a filter spec string into a NC_FILTER_SPEC*
87 
88 @param txt - a string containing the spec as a sequence of
89               constants separated by commas.
90 @param specp - store the parsed filter here -- caller frees
91 @return NC_NOERR if parse succeeded
92 @return NC_EINVAL otherwise
93 */
94 
95 EXTERNL int
NC_parsefilterspec(const char * txt,int format,NC_Filterspec ** specp)96 NC_parsefilterspec(const char* txt, int format, NC_Filterspec** specp)
97 {
98     int stat = NC_NOERR;
99     int sstat; /* for scanf */
100     char* p;
101     char* sdata0 = NULL; /* what to free */
102     char* sdata = NULL; /* sdata0 with leading prefix skipped */
103     unsigned int id;
104     size_t count; /* no. of comma delimited params */
105     size_t nparams; /* final no. of unsigned ints */
106     size_t len;
107     int i;
108     unsigned int* ulist = NULL;
109     unsigned char mem[8];
110 
111     if(txt == NULL) goto fail;
112     len = strlen(txt);
113     if(len == 0) goto fail;
114 
115     sdata0 = (char*)calloc(1,len+1+1);
116     if(sdata0 == NULL) return 0;
117     memcpy(sdata0,txt,len);
118     sdata = sdata0;
119 
120     /* Count number of parameters + id and delimit */
121     p=sdata;
122     for(count=0;;count++) {
123         char* q = strchr(p,',');
124 	if(q == NULL) break;
125 	*q++ = '\0';
126 	p = q;
127     }
128     count++; /* for final piece */
129 
130     if(count == 0)
131 	goto fail; /* no id and no parameters */
132 
133     /* Extract the filter id */
134     p = sdata;
135     if(strchr(NAMECHAR1,*p) != NULL) {
136 	const struct FilterName* candidate = known_filters;
137 	char* q = p+1;
138 	while(*q && strchr(NAMECHARN,*q) != NULL) {
139 	    q++;
140 	}
141 	if(*q) goto fail; /*name has bad char*/
142 	/* Lookup name */
143 	id = 0; /* => not found */
144 	for(;candidate->name;candidate++) {
145 	    if(strcasecmp(candidate->name,p)==0) {id = candidate->id; break;}
146 	}
147 	if(id == 0) goto fail; /* unknown name */
148     } else if(strchr(NUMCHAR,*p) != NULL) {
149 	sstat = sscanf(p,"%u",&id);
150 	if(sstat != 1) goto fail;
151     } else goto fail; /* unparseable id */
152     count--;
153 
154     /* skip past the filter id */
155     p = p + strlen(p) + 1;
156 
157     /* Allocate the max needed space; *2 in case the params are all doubles */
158     ulist = (unsigned int*)malloc(sizeof(unsigned int)*(count)*2);
159     if(ulist == NULL) goto fail;
160 
161     /* walk and convert */
162     nparams = 0; /* actual count */
163     for(i=0;i<count;i++) { /* step thru param strings */
164 	unsigned long long val64u;
165 	unsigned int val32u;
166 	double vald;
167 	float valf;
168 	unsigned int *vector;
169 	int isunsigned = 0;
170 	int isnegative = 0;
171 	int type = 0;
172 	char* q;
173 
174 	len = strlen(p);
175 	/* skip leading white space */
176 	while(strchr(" 	",*p) != NULL) {p++; len--;}
177 	/* Get leading sign character, if any */
178 	if(*p == '-') isnegative = 1;
179         /* Get trailing type tag characters */
180 	switch (len) {
181 	case 0:
182 	    goto fail; /* empty parameter */
183 	case 1:
184 	case 2:
185 	    q = (p + len) - 1; /* point to last char */
186 	    type = gettype(*q,'\0',&isunsigned);
187 	    break;
188 	default: /* > 2 => we might have a two letter tag */
189 	    q = (p + len) - 2;
190 	    type = gettype(*q,*(q+1),&isunsigned);
191 	    break;
192 	}
193 
194 	/* Now parse */
195 	switch (type) {
196 	case 'b':
197 	case 's':
198 	case 'i':
199  	    /* special case for a positive integer;for back compatibility.*/
200 	    if(!isnegative)
201 	        sstat = sscanf(p,"%u",&val32u);
202 	    else
203                 sstat = sscanf(p,"%d",(int*)&val32u);
204 	    if(sstat != 1) goto fail;
205 	    switch(type) {
206 	    case 'b': val32u = (val32u & 0xFF); break;
207 	    case 's': val32u = (val32u & 0xFFFF); break;
208 	    }
209 	    ulist[nparams++] = val32u;
210 	    break;
211 
212 	case 'f':
213 	    sstat = sscanf(p,"%lf",&vald);
214 	    if(sstat != 1) goto fail;
215 	    valf = (float)vald;
216 	    ulist[nparams++] = *(unsigned int*)&valf;
217 	    break;
218 
219 	/* The following are 8-byte values, so we must swap pieces if this
220            is a little endian machine */
221 	case 'd':
222 	    sstat = sscanf(p,"%lf",&vald);
223 	    if(sstat != 1) goto fail;
224 	    memcpy(mem,&vald,sizeof(mem));
225 	    NC4_filterfix8(mem,0);
226 	    vector = (unsigned int*)mem;
227 	    ulist[nparams++] = vector[0];
228 	    ulist[nparams++] = vector[1];
229 	    break;
230 	case 'l': /* long long */
231 	    if(isunsigned)
232 	        sstat = sscanf(p,"%llu",&val64u);
233 	    else
234                 sstat = sscanf(p,"%lld",(long long*)&val64u);
235 	    if(sstat != 1) goto fail;
236 	    memcpy(mem,&val64u,sizeof(mem));
237 	    NC4_filterfix8(mem,0);
238 	    vector = (unsigned int*)&mem;
239 	    ulist[nparams++] = vector[0];
240 	    ulist[nparams++] = vector[1];
241 	    break;
242 	default:
243 	    goto fail;
244 	}
245         p = p + strlen(p) + 1; /* move to next param */
246     }
247     /* Now return results */
248     if(specp != NULL) {
249         NC4_Filterspec* pfs = calloc(1,sizeof(NC4_Filterspec));
250         if(pfs == NULL) {stat = NC_ENOMEM; goto done;}
251         pfs->hdr.hdr.format = format;
252         pfs->filterid = id;
253         pfs->nparams = nparams;
254         pfs->params = ulist; ulist = NULL;
255 	*specp = (NC_Filterspec*)pfs;
256     }
257 
258 done:
259     if(sdata) free(sdata);
260     if(ulist) free(ulist);
261     return stat;
262 fail:
263     stat = NC_EINVAL;
264     goto done;
265 }
266 
267 /*
268 Parse a string containing multiple '|' separated filter specs.
269 
270 @param spec0 - a string containing the list of filter specs.
271 @param nspecsp - # of parsed specs
272 @param specsp - pointer to hold vector of parsed specs. Caller frees
273 @return NC_NOERR if parse succeeded
274 @return NC_EINVAL if bad parameters or parse failed
275 */
276 
277 EXTERNL int
NC_parsefilterlist(const char * txt0,int * formatp,size_t * nspecsp,NC_Filterspec *** vectorp)278 NC_parsefilterlist(const char* txt0, int* formatp, size_t* nspecsp, NC_Filterspec*** vectorp)
279 {
280     int stat = NC_NOERR;
281     int format = NC_FILTER_FORMAT_HDF5; /* default */
282     size_t len = 0;
283     size_t nspecs = 0;
284     NC4_Filterspec** vector = NULL;
285     char* spec0 = NULL; /* with prefix */
286     char* spec = NULL; /* without prefix */
287     char* p = NULL;
288     char* q = NULL;
289 
290     if(txt0  == NULL) return NC_EINVAL;
291     /* Duplicate txt0 so we can modify it */
292     len = strlen(txt0);
293     if((spec = calloc(1,len+1+1)) == NULL) return NC_ENOMEM;
294     memcpy(spec,txt0,len); /* Note double ending nul */
295     spec0 = spec; /* Save for later free */
296 
297     /* See if there is a prefix '[format]' tag */
298     if(spec[0] == LBRACK) {
299 	int found = 0;
300         const struct LegalFormat* candidates = legalformats;
301 	p = spec + 1;
302 	q = strchr(p,RBRACK);
303 	if(q == NULL) {stat = NC_EINVAL; goto done;}
304 	*q++ = '\0'; /* delimit tag */
305 	while(candidates->tag != NULL) {
306 	    if(strcasecmp(p,candidates->tag) == 0) {
307 		found = 1;
308 		format = candidates->format;
309 	    }
310 	}
311         if(found == 0) {stat = NC_EINVAL; goto done;}
312 	spec = q; /* skip tag wrt later processing */
313     }
314     if(formatp) *formatp = format;
315 
316     if(format != NC_FILTER_FORMAT_HDF5) {stat = NC_EINVAL; goto done;} /* for now */
317 
318     /* pass 1: count number of specs */
319     p = spec;
320     nspecs = 0;
321     while(*p) {
322 	q = strchr(p,'|');
323 	if(q == NULL) q = p + strlen(p); /* fake it */
324 	nspecs++;
325 	p = q + 1;
326     }
327     if(nspecs >  0) {
328 	int count = 0;
329 	if((vector = (NC4_Filterspec**)malloc(sizeof(NC4_Filterspec*)*nspecs)) == NULL)
330 	    {stat = NC_ENOMEM; goto done;}
331 	/* pass 2: parse */
332 	p = spec;
333 	for(count=0;count<nspecs;count++) {
334 	    NC4_Filterspec* aspec = NULL;
335 	    q = strchr(p,'|');
336 	    if(q == NULL) q = p + strlen(p); /* fake it */
337 	    *q = '\0';
338 	    if(NC_parsefilterspec(p,format,(NC_Filterspec**)&aspec))
339 	        {stat = NC_EINVAL; goto done;}
340 	    vector[count] = aspec; aspec = NULL;
341 	    p = q+1; /* ok because of double nul */
342 	}
343     }
344     if(nspecsp) *nspecsp = nspecs;
345     if(vectorp) *vectorp = (nspecs == 0 ? NULL : (NC_Filterspec**)vector);
346     vector = NULL;
347 done:
348     nullfree(spec0);
349     if(vector != NULL) {
350 	int k;
351 	for(k=0;k<nspecs;k++) {
352 	    NC4_Filterspec* nfs = vector[k];
353 	    if(nfs->params) free(nfs->params);
354 	    nullfree(nfs);
355 	}
356 	free(vector);
357     }
358     return stat;
359 }
360 
361 
362 #ifdef WORDS_BIGENDIAN
363 /* Byte swap an 8-byte integer in place */
364 static void
byteswap8(unsigned char * mem)365 byteswap8(unsigned char* mem)
366 {
367     unsigned char c;
368     c = mem[0];
369     mem[0] = mem[7];
370     mem[7] = c;
371     c = mem[1];
372     mem[1] = mem[6];
373     mem[6] = c;
374     c = mem[2];
375     mem[2] = mem[5];
376     mem[5] = c;
377     c = mem[3];
378     mem[3] = mem[4];
379     mem[4] = c;
380 }
381 
382 /* Byte swap an 8-byte integer in place */
383 static void
byteswap4(unsigned char * mem)384 byteswap4(unsigned char* mem)
385 {
386     unsigned char c;
387     c = mem[0];
388     mem[0] = mem[3];
389     mem[3] = c;
390     c = mem[1];
391     mem[1] = mem[2];
392     mem[2] = c;
393 }
394 #endif
395 
396 
397 EXTERNL void
NC4_filterfix8(unsigned char * mem,int decode)398 NC4_filterfix8(unsigned char* mem, int decode)
399 {
400 #ifdef WORDS_BIGENDIAN
401     if(decode) { /* Apply inverse of the encode case */
402 	byteswap4(mem); /* step 1: byte-swap each piece */
403 	byteswap4(mem+4);
404 	byteswap8(mem); /* step 2: convert to little endian format */
405     } else { /* encode */
406 	byteswap8(mem); /* step 1: convert to little endian format */
407 	byteswap4(mem); /* step 2: byte-swap each piece */
408 	byteswap4(mem+4);
409     }
410 #else /* Little endian */
411     /* No action is necessary */
412 #endif
413 }
414 
415 
416 /* Support direct user defined filters */
417 
418 /* Use void* to avoid having to include hdf.h*/
419 EXTERNL int
nc_filter_client_register(unsigned int id,void * info)420 nc_filter_client_register(unsigned int id, void* info)
421 {
422     int stat = NC_NOERR;
423     if(id == 0 ||info == NULL)
424 	return NC_EINVAL;
425 #ifdef USE_HDF5
426     NC_FILTER_OBJ_HDF5 client;
427     memset(&client,0,sizeof(client));
428     client.hdr.format = NC_FILTER_FORMAT_HDF5;
429     client.sort = NC_FILTER_SORT_CLIENT;
430     client.u.client.id = id;
431     client.u.client.info = info;
432     /* Note use of a global function, not part of the dispatch table */
433     stat = nc4_global_filter_action(NCFILTER_CLIENT_REG, id, &client);
434 #else
435     stat = NC_ENOTBUILT;
436 #endif
437     return stat;
438 }
439 
440 EXTERNL int
nc_filter_client_unregister(unsigned int id)441 nc_filter_client_unregister(unsigned int id)
442 {
443     int stat = NC_NOERR;
444 #ifdef USE_HDF5
445     stat = nc4_global_filter_action(NCFILTER_CLIENT_UNREG, id, NULL);
446 #else
447     stat = NC_ENOTBUILT;
448 #endif
449     return stat;
450 }
451 
452 /* Use void* to avoid having to include hdf.h*/
453 EXTERNL int
nc_filter_client_inq(unsigned int id,void * infop)454 nc_filter_client_inq(unsigned int id, void* infop)
455 {
456     int stat = NC_NOERR;
457 #ifdef USE_HDF5
458     H5Z_class2_t* hct = (H5Z_class2_t*)infop;
459     NC_FILTER_OBJ_HDF5 client;
460     if(id == 0 ||infop == NULL)
461 	return NC_EINVAL;
462     memset(&client,0,sizeof(client));
463     client.hdr.format = NC_FILTER_FORMAT_HDF5;
464     client.sort = NC_FILTER_SORT_CLIENT;
465     client.u.client.id = id;
466     client.u.client.info = hct;
467     /* Note use of a global function, not part of the dispatch table */
468     stat = nc4_global_filter_action(NCFILTER_CLIENT_INQ, id, &client);
469     if(stat == NC_NOERR) {
470 	*hct = *(H5Z_class2_t*)client.u.client.info;
471     }
472 #else
473     stat = NC_ENOTBUILT;
474 #endif
475     return stat;
476 }
477 
478 /**
479 Find the set of filters (if any) associated with a variable.
480 
481 \param ncid NetCDF or group ID, from a previous call to nc_open(),
482 nc_create(), nc_def_grp(), or associated inquiry functions such as
483 nc_inq_ncid().
484 
485 \param varid Variable ID
486 \param nfilters return no. of filters
487 \param ids return the filter ids (caller allocates)
488 
489 \returns ::NC_NOERR No error.
490 \returns ::NC_ENOTNC4 Not a netCDF-4 file.
491 \returns ::NC_EBADID Bad ncid.
492 \returns ::NC_ENOTVAR Invalid variable ID.
493 \returns ::NC_EINVAL Invalid arguments
494 \ingroup variables
495 \author Dennis Heimbigner
496 */
497 EXTERNL int
nc_inq_var_filterids(int ncid,int varid,size_t * nfiltersp,unsigned int * ids)498 nc_inq_var_filterids(int ncid, int varid, size_t* nfiltersp, unsigned int* ids)
499 {
500    NC* ncp;
501    int stat = NC_check_id(ncid,&ncp);
502    NC_FILTER_OBJ_HDF5 ncids;
503 
504    if(stat != NC_NOERR) return stat;
505    TRACE(nc_inq_var_filterids);
506 
507    memset(&ncids,0,sizeof(ncids));
508    ncids.hdr.format = NC_FILTER_FORMAT_HDF5;
509    ncids.sort = NC_FILTER_SORT_IDS;
510    ncids.u.ids.nfilters = (nfiltersp?*nfiltersp:0);
511    ncids.u.ids.filterids = ids;
512 
513    if((stat = ncp->dispatch->filter_actions(ncid,varid, NCFILTER_FILTERIDS, (NC_Filterobject*)&ncids)) == NC_NOERR) {
514        if(nfiltersp) *nfiltersp = ncids.u.ids.nfilters;
515    }
516    return stat;
517  }
518 
519 /**
520 Find the the param info about filter (if any)
521 associated with a variable and with specified id.
522 
523 This is a wrapper for nc_inq_var_all().
524 
525 \param ncid NetCDF or group ID, from a previous call to nc_open(),
526 nc_create(), nc_def_grp(), or associated inquiry functions such as
527 nc_inq_ncid().
528 
529 \param varid Variable ID
530 \param id The filter id of interest
531 \param formatp (Out) Storage for the filter format
532 \param nparamsp (Out) Storage which will get the number of parameters to the filter
533 \param params (Out) Storage which will get associated parameters.
534 Note: the caller must allocate and free.
535 
536 \returns ::NC_NOERR No error.
537 \returns ::NC_ENOTNC4 Not a netCDF-4 file.
538 \returns ::NC_EBADID Bad ncid.
539 \returns ::NC_ENOTVAR Invalid variable ID.
540 \returns ::NC_ENOFILTER No filter defined.
541 \ingroup variables
542 \author Dennis Heimbigner
543 */
544 EXTERNL int
nc_inq_var_filter_info(int ncid,int varid,unsigned int id,size_t * nparamsp,unsigned int * params)545 nc_inq_var_filter_info(int ncid, int varid, unsigned int id, size_t* nparamsp, unsigned int* params)
546 {
547    NC* ncp;
548    int stat = NC_check_id(ncid,&ncp);
549    NC_FILTER_OBJ_HDF5 spec;
550 
551    if(stat != NC_NOERR) return stat;
552    TRACE(nc_inq_var_filter_info_hdf5);
553 
554    memset(&spec,0,sizeof(spec));
555    spec.hdr.format = NC_FILTER_FORMAT_HDF5;
556    spec.sort = NC_FILTER_SORT_SPEC;
557    spec.u.spec.filterid = id;
558    spec.u.spec.nparams = (nparamsp?*nparamsp:0);
559    spec.u.spec.params = params;
560 
561    if((stat = ncp->dispatch->filter_actions(ncid,varid,NCFILTER_INFO,(NC_Filterobject*)&spec)) == NC_NOERR) {
562        if(nparamsp) *nparamsp = spec.u.spec.nparams;
563    }
564    return stat;
565 }
566 
567 /**
568 Find the first filter (if any) associated with a variable.
569 
570 \param ncid NetCDF or group ID, from a previous call to nc_open(),
571 nc_create(), nc_def_grp(), or associated inquiry functions such as
572 nc_inq_ncid().
573 
574 \param varid Variable ID
575 
576 \param idp Storage which will get the filter id; a return value of zero means no filter
577 
578 \param nparamsp Storage which will get the number of parameters to the
579 filter
580 
581 \param params Storage which will get associated parameters.
582 Note: the caller must allocate and free.
583 
584 \returns ::NC_NOERR No error.
585 \returns ::NC_ENOTNC4 Not a netCDF-4 file.
586 \returns ::NC_EBADID Bad ncid.
587 \returns ::NC_ENOTVAR Invalid variable ID.
588 \returns ::NC_ENOFILTER No filter defined.
589 \ingroup variables
590 \author Dennis Heimbigner
591 */
592 EXTERNL int
nc_inq_var_filter(int ncid,int varid,unsigned int * idp,size_t * nparamsp,unsigned int * params)593 nc_inq_var_filter(int ncid, int varid, unsigned int* idp, size_t* nparamsp, unsigned int* params)
594 {
595    NC* ncp;
596    NC_FILTER_OBJ_HDF5 spec;
597    int stat = NC_check_id(ncid,&ncp);
598 
599    if(stat != NC_NOERR) return stat;
600    TRACE(nc_inq_var_filter);
601 
602    memset(&spec,0,sizeof(spec));
603    spec.hdr.format = NC_FILTER_FORMAT_HDF5;
604    spec.sort = NC_FILTER_SORT_SPEC;
605    spec.u.spec.filterid = (idp?*idp:0);
606    spec.u.spec.nparams = (nparamsp?*nparamsp:0);
607    spec.u.spec.params = params;
608 
609    if((stat=ncp->dispatch->filter_actions(ncid,varid,NCFILTER_INQ,(NC_Filterobject*)&spec)))
610       return stat;
611    if(idp) *idp = spec.u.spec.filterid;
612    if(nparamsp) *nparamsp = spec.u.spec.nparams;
613    return stat;
614 }
615 
616 /**
617    Define a new variable hdf5 filter.
618 
619    Only variables with chunked storage can use filters.
620 
621    @param ncid File and group ID.
622    @param varid Variable ID.
623    @param id Filter ID.
624    @param nparams Number of filter parameters.
625    @param parms Filter parameters.
626 
627    @return ::NC_NOERR No error.
628    @return ::NC_EINVAL Variable must be chunked.
629    @return ::NC_EBADID Bad ID.
630    @author Dennis Heimbigner
631 */
632 
633 EXTERNL int
nc_def_var_filter(int ncid,int varid,unsigned int id,size_t nparams,const unsigned int * params)634 nc_def_var_filter(int ncid, int varid, unsigned int id, size_t nparams, const unsigned int* params)
635 {
636     NC* ncp;
637     NC_FILTER_OBJ_HDF5 spec;
638     int stat = NC_check_id(ncid,&ncp);
639 
640     if(stat != NC_NOERR) return stat;
641     TRACE(nc_def_var_filter_hdf5);
642 
643     memset(&spec,0,sizeof(spec));
644     spec.hdr.format = NC_FILTER_FORMAT_HDF5;
645     spec.sort = NC_FILTER_SORT_SPEC;
646     spec.u.spec.filterid = id;
647     spec.u.spec.nparams = nparams;
648     spec.u.spec.params = (unsigned int*)params; /* discard const */
649     return ncp->dispatch->filter_actions(ncid,varid,NCFILTER_DEF,(NC_Filterobject*)&spec);
650 }
651 
652 /**
653    Remove all filters with specified id from a variable
654 
655    @param ncid File and group ID.
656    @param varid Variable ID.
657    @param id filter to remove
658 
659    @return ::NC_NOERR No error.
660    @return ::NC_EBADID Bad ID.
661    @author Dennis Heimbigner
662 */
663 EXTERNL int
nc_var_filter_remove(int ncid,int varid,unsigned int id)664 nc_var_filter_remove(int ncid, int varid, unsigned int id)
665 {
666     NC* ncp;
667     int stat = NC_check_id(ncid,&ncp);
668     NC_FILTER_OBJ_HDF5 spec;
669 
670     if(stat != NC_NOERR) return stat;
671     TRACE(nc_var_filter_hdf5_remove);
672 
673     memset(&spec,0,sizeof(spec));
674     spec.hdr.format = NC_FILTER_FORMAT_HDF5;
675     spec.sort = NC_FILTER_SORT_SPEC;
676     spec.u.spec.filterid = id;
677     return ncp->dispatch->filter_actions(ncid,varid,NCFILTER_REMOVE,(NC_Filterobject*)&spec);
678 }
679 
680 /**************************************************/
681 /* Utilities */
682 
683 /* Look at q0 and q1) to determine type */
684 static int
gettype(const int q0,const int q1,int * isunsignedp)685 gettype(const int q0, const int q1, int* isunsignedp)
686 {
687     int type = 0;
688     int isunsigned = 0;
689     char typechar;
690 
691     isunsigned = (q0 == 'u' || q0 == 'U');
692     if(q1 == '\0')
693 	typechar = q0; /* we were given only a single char */
694     else if(isunsigned)
695 	typechar = q1; /* we have something like Ux as the tag */
696     else
697 	typechar = q1; /* look at last char for tag */
698     switch (typechar) {
699     case 'f': case 'F': case '.': type = 'f'; break; /* float */
700     case 'd': case 'D': type = 'd'; break; /* double */
701     case 'b': case 'B': type = 'b'; break; /* byte */
702     case 's': case 'S': type = 's'; break; /* short */
703     case 'l': case 'L': type = 'l'; break; /* long long */
704     case '0': case '1': case '2': case '3': case '4':
705     case '5': case '6': case '7': case '8': case '9': type = 'i'; break;
706     case 'u': case 'U': type = 'i'; isunsigned = 1; break; /* unsigned int */
707     case '\0': type = 'i'; break;
708     default: break;
709     }
710     if(isunsignedp) *isunsignedp = isunsigned;
711     return type;
712 }
713