1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
/*********************************************************************
* Copyright 2010, UCAR/Unidata
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
* $Id $
*********************************************************************/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <netcdf.h>
#include "utils.h"
#include "chunkspec.h"
/* Structure mapping dimension IDs to corresponding chunksizes. */
static struct {
size_t ndims; /* number of dimensions in chunkspec string */
int *dimids; /* ids for dimensions in chunkspec string */
size_t *chunksizes; /* corresponding chunk sizes */
bool_t omit; /* true if chunking to be turned off */
} chunkspecs;
/*
* Parse chunkspec string and convert into chunkspec_t structure.
* ncid: location ID of open netCDF file or group in an open file
* spec: string of form
* dim1/n1,dim2/n2,...,dimk/nk
*
* specifying chunk size (ni) to be used for dimension named
* dimi. Dimension names may be absolute,
* e.g. "/grp_a/grp_a1/dim". The "ni" part of the spec may be
* omitted, in which case it is assumed to be the entire
* dimension size. That is also the default for dimensions
* not mentioned in the string.
* If the chunkspec string is "/", specifying no dimensions or
* chunk sizes, it indicates chunking to be turned off on output.
*
* Returns NC_NOERR if no error, NC_EINVAL if spec has consecutive
* unescaped commas or no chunksize specified for dimension.
*/
int
chunkspec_parse(int ncid, const char *spec) {
const char *cp; /* character cursor */
const char *pp = spec; /* previous char cursor for detecting escapes */
const char *np; /* beginning of current dimension name */
size_t ndims = 0;
int idim;
int ret;
int comma_seen = 0;
chunkspecs.ndims = 0;
chunkspecs.omit = false;
if (!spec || *spec == '\0') /* default chunking */
return NC_NOERR;
if (spec[0] == '/' && spec[1] == '\0') { /* no chunking */
chunkspecs.omit = true;
return NC_NOERR;
}
/* Count unescaped commas, handle consecutive unescaped commas as error */
for(cp = spec; *cp; cp++) {
if(*cp == ',' && *pp != '\\') {
if(comma_seen) { /* consecutive commas detected */
return(NC_EINVAL);
}
comma_seen = 1;
ndims++;
} else {
comma_seen = 0;
}
pp = cp;
}
ndims++;
chunkspecs.ndims = ndims;
chunkspecs.dimids = (int *) emalloc(ndims * sizeof(int));
chunkspecs.chunksizes = (size_t *) emalloc(ndims * sizeof(size_t));
/* Look up dimension ids and assign chunksizes */
pp = spec;
np = spec;
idim = 0;
for(cp = spec; ; cp++) {
if(*cp == '\0' || (*cp == ',' && *pp != '\\')) { /* found end of "dim/nn" part */
char* dimname = 0;
char *dp;
int dimid;
size_t chunksize;
for(; pp > np && *pp != '/'; pp--) { /* look backwards for "/" */
continue;
}
if(*pp != '/') { /* no '/' found, no chunksize specified for dimension */
return(NC_EINVAL);
}
/* extract dimension name */
dimname = (char *) emalloc(pp - np + 1);
dp = dimname;
while(np < pp) {
*dp++ = *np++;
}
*dp = '\0';
/* look up dimension id from dimension pathname */
ret = nc_inq_dimid2(ncid, dimname, &dimid);
if(ret != NC_NOERR)
break;
chunkspecs.dimids[idim] = dimid;
/* parse and assign corresponding chunksize */
pp++; /* now points to first digit of chunksize, ',', or '\0' */
if(*pp == ',' || *pp == '\0') { /* no size specified, use dim len */
size_t dimlen;
ret = nc_inq_dimlen(ncid, dimid, &dimlen);
if(ret != NC_NOERR)
return(ret);
chunksize = dimlen;
} else { /* convert nnn string to long long integer */
char *ep;
#ifdef HAVE_STRTOLL
long long val = strtoll(pp, &ep, 0);
#else
long long val = strtol(pp, &ep, 0);
#endif
if(ep == pp || errno == ERANGE || val < 1) /* allow chunksize bigger than dimlen */
return (NC_EINVAL);
chunksize = (size_t)val;
}
chunkspecs.chunksizes[idim] = chunksize;
idim++;
free(dimname);
if(*cp == '\0')
break;
/* set np to point to first char after comma */
np = cp + 1;
}
pp = cp;
};
return NC_NOERR;
}
/* Return size in chunkspec string specified for dimension corresponding to dimid, 0 if not found */
size_t
chunkspec_size(int dimid) {
int idim;
for(idim = 0; idim < chunkspecs.ndims; idim++) {
if(dimid == chunkspecs.dimids[idim]) {
return chunkspecs.chunksizes[idim];
}
}
return 0;
}
/* Return number of dimensions for which chunking was specified in
* chunkspec string on command line, 0 if no chunkspec string was
* specified. */
int
chunkspec_ndims(void) {
return chunkspecs.ndims;
}
/* Return whether chunking should be omitted, due to explicit
* command-line specification. */
bool_t
chunkspec_omit(void) {
return chunkspecs.omit;
}
|