mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-11-27 07:30:33 +08:00
714 lines
14 KiB
C
714 lines
14 KiB
C
/*********************************************************************
|
|
* Copyright 1993, UCAR/Unidata
|
|
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
|
|
* $Header: /upc/share/CVS/netcdf-3/ncgen/util.c,v 1.4 2010/04/14 22:04:59 dmh Exp $
|
|
*********************************************************************/
|
|
|
|
#include "includes.h"
|
|
|
|
/* Track primitive symbol instances (initialized in ncgen.y) */
|
|
Symbol* primsymbols[PRIMNO];
|
|
|
|
char*
|
|
append(const char* s1, const char* s2)
|
|
{
|
|
int len = (s1?strlen(s1):0)+(s2?strlen(s2):0);
|
|
char* result = (char*)ecalloc(len+1);
|
|
result[0] = '\0';
|
|
if(s1) strcat(result,s1);
|
|
if(s2) strcat(result,s2);
|
|
return result;
|
|
}
|
|
|
|
|
|
unsigned int
|
|
chartohex(char c)
|
|
{
|
|
switch (c) {
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
return (c - '0');
|
|
case 'A': case 'B': case 'C':
|
|
case 'D': case 'E': case 'F':
|
|
return (c - 'A') + 0x0a;
|
|
case 'a': case 'b': case 'c':
|
|
case 'd': case 'e': case 'f':
|
|
return (c - 'a') + 0x0a;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* For generated Fortran, change 'e' to 'd' in exponent of double precision
|
|
* constants.
|
|
*/
|
|
void
|
|
expe2d(
|
|
char *cp) /* string containing double constant */
|
|
{
|
|
char *expchar = strrchr(cp,'e');
|
|
if (expchar) {
|
|
*expchar = 'd';
|
|
}
|
|
}
|
|
|
|
/* Returns non-zero if n is a power of 2, 0 otherwise */
|
|
int
|
|
pow2(
|
|
int n)
|
|
{
|
|
int m = n;
|
|
int p = 1;
|
|
|
|
while (m > 0) {
|
|
m /= 2;
|
|
p *= 2;
|
|
}
|
|
return p == 2*n;
|
|
}
|
|
|
|
|
|
/*
|
|
* Remove trailing zeros (after decimal point) but not trailing decimal
|
|
* point from ss, a string representation of a floating-point number that
|
|
* might include an exponent part.
|
|
*/
|
|
void
|
|
tztrim(
|
|
char *ss /* returned string representing dd */
|
|
)
|
|
{
|
|
char *cp, *ep;
|
|
|
|
cp = ss;
|
|
if (*cp == '-')
|
|
cp++;
|
|
while(isdigit((int)*cp) || *cp == '.')
|
|
cp++;
|
|
if (*--cp == '.')
|
|
return;
|
|
ep = cp+1;
|
|
while (*cp == '0')
|
|
cp--;
|
|
cp++;
|
|
if (cp == ep)
|
|
return;
|
|
while (*ep)
|
|
*cp++ = *ep++;
|
|
*cp = '\0';
|
|
return;
|
|
}
|
|
|
|
/* Assume bytebuffer contains pointers to char**/
|
|
void
|
|
reclaimattptrs(void* buf, long count)
|
|
{
|
|
int i;
|
|
char** ptrs = (char**)buf;
|
|
for(i=0;i<count;i++) {free((void*)ptrs[i]);}
|
|
}
|
|
|
|
void
|
|
freeSymbol(Symbol* sym)
|
|
{
|
|
#ifdef FIX
|
|
switch (sym->objectclass) {
|
|
case NG_VAR:
|
|
reclaimconstlist(vsym->var.data);
|
|
if(vsym->var.dims != NULL) efree(vsym->var.dims);
|
|
break;
|
|
case NG_ATT:
|
|
if(asym->att.basetype == primsymbols[NC_STRING])
|
|
reclaimattptrs(asym->att.data,asym->att.count);
|
|
else
|
|
efree(asym->att.data);
|
|
break;
|
|
case NG_GRP:
|
|
case NG_DIM:
|
|
case NG_TYP:
|
|
case NG_ENUM:
|
|
case NG_ECONST:
|
|
case NG_VLEN:
|
|
case NG_STRUCT:
|
|
case NG_FIELD:
|
|
case NG_OPAQUE:
|
|
default: break;
|
|
}
|
|
efree(sym->name);
|
|
efree(sym);
|
|
#endif
|
|
}
|
|
|
|
char* nctypenames[17] = {
|
|
"NC_NAT",
|
|
"NC_BYTE", "NC_CHAR", "NC_SHORT", "NC_INT",
|
|
"NC_FLOAT", "NC_DOUBLE",
|
|
"NC_UBYTE", "NC_USHORT", "NC_UINT",
|
|
"NC_INT64", "NC_UINT64",
|
|
"NC_STRING",
|
|
"NC_VLEN", "NC_OPAQUE", "NC_ENUM", "NC_COMPOUND"
|
|
};
|
|
|
|
char* nctypenamesextend[9] = {
|
|
"NC_GRP", "NC_DIM", "NC_VAR", "NC_ATT", "NC_TYPE",
|
|
"NC_ECONST","NC_FIELD", "NC_ARRAY","NC_PRIM"
|
|
};
|
|
|
|
char*
|
|
nctypename(nc_type nctype)
|
|
{
|
|
char* s;
|
|
if(nctype >= NC_NAT && nctype <= NC_COMPOUND)
|
|
return nctypenames[nctype];
|
|
if(nctype >= NC_GRP && nctype <= NC_PRIM)
|
|
return nctypenamesextend[(nctype - NC_GRP)];
|
|
if(nctype == NC_FILLVALUE) return "NC_FILL";
|
|
if(nctype == NC_NIL) return "NC_NIL";
|
|
s = poolalloc(128);
|
|
sprintf(s,"NC_<%d>",nctype);
|
|
return s;
|
|
}
|
|
|
|
/* These are the augmented NC_ values (0 based from NC_GRP)*/
|
|
char* ncclassnames[9] = {
|
|
"NC_GRP", "NC_DIM", "NC_VAR", "NC_ATT",
|
|
"NC_TYP", "NC_ECONST", "NC_FIELD", "NC_ARRAY",
|
|
"NC_PRIM"
|
|
};
|
|
|
|
char*
|
|
ncclassname(nc_class ncc)
|
|
{
|
|
char* s;
|
|
if(ncc >= NC_NAT && ncc <= NC_COMPOUND)
|
|
return nctypename((nc_type)ncc);
|
|
if(ncc == NC_FILLVALUE) return "NC_FILL";
|
|
if(ncc >= NC_GRP && ncc <= NC_PRIM)
|
|
return ncclassnames[ncc - NC_GRP];
|
|
s = poolalloc(128);
|
|
sprintf(s,"NC_<%d>",ncc);
|
|
return s;
|
|
}
|
|
|
|
int ncsizes[17] = {
|
|
0,
|
|
1,1,2,4,
|
|
4,8,
|
|
1,2,4,
|
|
8,8,
|
|
sizeof(char*),
|
|
sizeof(nc_vlen_t),
|
|
0,0,0
|
|
};
|
|
|
|
int
|
|
ncsize(nc_type nctype)
|
|
{
|
|
if(nctype >= NC_NAT && nctype <= NC_COMPOUND)
|
|
return ncsizes[nctype];
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
hasunlimited(Dimset* dimset)
|
|
{
|
|
int i;
|
|
for(i=0;i<dimset->ndims;i++) {
|
|
Symbol* dim = dimset->dimsyms[i];
|
|
if(dim->dim.declsize == NC_UNLIMITED) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* return 1 if first dimension is unlimited*/
|
|
int
|
|
isunlimited0(Dimset* dimset)
|
|
{
|
|
return (dimset->ndims > 0 && dimset->dimsyms[0]->dim.declsize == NC_UNLIMITED);
|
|
}
|
|
|
|
|
|
/* True only if dim[0] is unlimited all rest are bounded*/
|
|
/* or all are bounded*/
|
|
int
|
|
classicunlimited(Dimset* dimset)
|
|
{
|
|
int i;
|
|
int last = -1;
|
|
for(i=0;i<dimset->ndims;i++) {
|
|
Symbol* dim = dimset->dimsyms[i];
|
|
if(dim->dim.declsize == NC_UNLIMITED) last = i;
|
|
}
|
|
return (last < 1);
|
|
}
|
|
|
|
/* True only iff no dimension is unlimited*/
|
|
int
|
|
isbounded(Dimset* dimset)
|
|
{
|
|
int i;
|
|
for(i=0;i<dimset->ndims;i++) {
|
|
Symbol* dim = dimset->dimsyms[i];
|
|
if(dim->dim.declsize == NC_UNLIMITED) return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
signedtype(nc_type nctype)
|
|
{
|
|
switch (nctype) {
|
|
case NC_BYTE:
|
|
case NC_SHORT:
|
|
case NC_INT:
|
|
case NC_INT64:
|
|
return nctype;
|
|
case NC_UBYTE: return NC_BYTE;
|
|
case NC_USHORT: return NC_SHORT;
|
|
case NC_UINT: return NC_INT;
|
|
case NC_UINT64: return NC_INT64;
|
|
default: break;
|
|
}
|
|
return nctype;
|
|
}
|
|
|
|
int
|
|
unsignedtype(nc_type nctype)
|
|
{
|
|
switch (nctype) {
|
|
case NC_UBYTE:
|
|
case NC_USHORT:
|
|
case NC_UINT:
|
|
case NC_UINT64:
|
|
return nctype;
|
|
case NC_BYTE: return NC_UBYTE;
|
|
case NC_SHORT: return NC_USHORT;
|
|
case NC_INT: return NC_UINT;
|
|
case NC_INT64: return NC_UINT64;
|
|
default: break;
|
|
}
|
|
return nctype;
|
|
}
|
|
|
|
int
|
|
isinttype(nc_type nctype)
|
|
{
|
|
return (nctype != NC_CHAR)
|
|
&& ((nctype >= NC_BYTE && nctype <= NC_INT)
|
|
|| (nctype >= NC_UBYTE && nctype <= NC_UINT64));
|
|
}
|
|
|
|
int
|
|
isuinttype(nc_type t)
|
|
{
|
|
return isinttype(t)
|
|
&& t >= NC_UBYTE
|
|
&& t <= NC_UINT64
|
|
&& t != NC_INT64;
|
|
}
|
|
|
|
int
|
|
isfloattype(nc_type nctype)
|
|
{
|
|
return (nctype == NC_FLOAT || nctype <= NC_DOUBLE);
|
|
}
|
|
|
|
int
|
|
isclassicprim(nc_type nctype)
|
|
{
|
|
return (nctype >= NC_BYTE && nctype <= NC_DOUBLE)
|
|
;
|
|
}
|
|
|
|
int
|
|
isclassicprimplus(nc_type nctype)
|
|
{
|
|
return (nctype >= NC_BYTE && nctype <= NC_DOUBLE)
|
|
|| (nctype == NC_STRING)
|
|
;
|
|
}
|
|
|
|
int
|
|
isprim(nc_type nctype)
|
|
{
|
|
return (nctype >= NC_BYTE && nctype <= NC_STRING)
|
|
;
|
|
}
|
|
|
|
int
|
|
isprimplus(nc_type nctype)
|
|
{
|
|
return (nctype >= NC_BYTE && nctype <= NC_STRING)
|
|
|| (nctype == NC_ECONST)
|
|
|| (nctype == NC_OPAQUE)
|
|
;
|
|
}
|
|
|
|
void
|
|
collectpath(Symbol* grp, List* grpstack)
|
|
{
|
|
while(grp != NULL) {
|
|
listpush(grpstack,(void*)grp);
|
|
grp = grp->container;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef USE_NETCDF4
|
|
/* Result is pool'd*/
|
|
char*
|
|
prefixtostring(List* prefix, char* separator)
|
|
{
|
|
int slen=0;
|
|
int plen;
|
|
int i;
|
|
char* result;
|
|
if(prefix == NULL) return pooldup("");
|
|
plen = prefixlen(prefix);
|
|
if(plen == 0) { /* root prefix*/
|
|
slen=0;
|
|
/* slen += strlen(separator);*/
|
|
slen++; /* for null terminator*/
|
|
result = poolalloc(slen);
|
|
result[0] = '\0';
|
|
/*strcat(result,separator);*/
|
|
} else {
|
|
for(i=0;i<plen;i++) {
|
|
Symbol* sym = (Symbol*)listget(prefix,i);
|
|
slen += (strlen(separator)+strlen(sym->name));
|
|
}
|
|
slen++; /* for null terminator*/
|
|
result = poolalloc(slen);
|
|
result[0] = '\0';
|
|
for(i=0;i<plen;i++) {
|
|
Symbol* sym = (Symbol*)listget(prefix,i);
|
|
strcat(result,separator);
|
|
strcat(result,sym->name); /* append "/<prefix[i]>"*/
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
/* Result is pool'd*/
|
|
char*
|
|
fullname(Symbol* sym)
|
|
{
|
|
#ifdef USE_NETCDF4
|
|
char* s1;
|
|
char* result;
|
|
char* prefix;
|
|
prefix = prefixtostring(sym->prefix,PATHSEPARATOR);
|
|
s1 = poolcat(prefix,PATHSEPARATOR);
|
|
result = poolcat(s1,sym->name);
|
|
return result;
|
|
#else
|
|
return nulldup(sym->name);
|
|
#endif
|
|
}
|
|
|
|
int
|
|
prefixeq(List* x1, List* x2)
|
|
{
|
|
Symbol** l1;
|
|
Symbol** l2;
|
|
int len,i;
|
|
if((len=listlength(x1)) != listlength(x2)) return 0;
|
|
l1=(Symbol**)listcontents(x1);
|
|
l2=(Symbol**)listcontents(x2);
|
|
for(i=0;i<len;i++) {
|
|
if(strcmp(l1[i]->name,l2[i]->name) != 0) return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
List*
|
|
prefixdup(List* prefix)
|
|
{
|
|
List* dupseq;
|
|
int i;
|
|
if(prefix == NULL) return listnew();
|
|
dupseq = listnew();
|
|
listsetalloc(dupseq,listlength(prefix));
|
|
for(i=0;i<listlength(prefix);i++) listpush(dupseq,listget(prefix,i));
|
|
return dupseq;
|
|
}
|
|
|
|
/*
|
|
Many of the generate routines need to construct
|
|
heap strings for short periods. Remembering to
|
|
free such space is error prone, so provide a
|
|
pseudo-GC to handle these short term requests.
|
|
The idea is to have a fixed size pool
|
|
tracking malloc requests and automatically
|
|
releasing when the pool gets full.
|
|
*/
|
|
|
|
/* Max number of allocated pool items*/
|
|
#define POOLMAX 100
|
|
|
|
static char* pool[POOLMAX];
|
|
static int poolindex = -1;
|
|
#define POOL_DEFAULT 256
|
|
|
|
char*
|
|
poolalloc(size_t length)
|
|
{
|
|
if(poolindex == -1) { /* initialize*/
|
|
memset((void*)pool,0,sizeof(pool));
|
|
poolindex = 0;
|
|
}
|
|
if(poolindex == POOLMAX) poolindex=0;
|
|
if(length == 0) length = POOL_DEFAULT;
|
|
if(pool[poolindex] != NULL) efree(pool[poolindex]);
|
|
pool[poolindex] = (char*)ecalloc(length);
|
|
return pool[poolindex++];
|
|
}
|
|
|
|
char*
|
|
pooldup(const char* s)
|
|
{
|
|
char* sdup = poolalloc(strlen(s)+1);
|
|
strncpy(sdup,s,(strlen(s)+1));
|
|
return sdup;
|
|
}
|
|
|
|
char*
|
|
poolcat(const char* s1, const char* s2)
|
|
{
|
|
int len1, len2;
|
|
char* cat;
|
|
if(s1 == NULL && s2 == NULL) return NULL;
|
|
len1 = (s1?strlen(s1):0);
|
|
len2 = (s2?strlen(s2):0);
|
|
cat = poolalloc(len1+len2+1);
|
|
cat[0] = '\0';
|
|
if(s1 != NULL) strcat(cat,s1);
|
|
if(s2 != NULL) strcat(cat,s2);
|
|
return cat;
|
|
}
|
|
|
|
/* Result is malloc'd*/
|
|
unsigned char*
|
|
makebytestring(char* s, size_t* lenp)
|
|
{
|
|
unsigned char* bytes;
|
|
unsigned char* b;
|
|
size_t slen = strlen(s); /* # nibbles */
|
|
size_t blen = slen/2; /* # bytes */
|
|
int i;
|
|
|
|
ASSERT((slen%2) == 0);
|
|
ASSERT(blen > 0);
|
|
bytes = (unsigned char*)ecalloc(blen);
|
|
b = bytes;
|
|
for(i=0;i<slen;i+=2) {
|
|
unsigned int digit1 = chartohex(*s++);
|
|
unsigned int digit2 = chartohex(*s++);
|
|
unsigned int byte = (digit1 << 4) | digit2;
|
|
*b++ = byte;
|
|
}
|
|
if(lenp) *lenp = blen;
|
|
return bytes;
|
|
}
|
|
|
|
int
|
|
getpadding(int offset, int alignment)
|
|
{
|
|
int rem = (alignment==0?0:(offset % alignment));
|
|
int pad = (rem==0?0:(alignment - rem));
|
|
return pad;
|
|
}
|
|
|
|
static void
|
|
reclaimSymbols(void)
|
|
{
|
|
Symbol* sym;
|
|
for(sym=symlist;sym;) {
|
|
Symbol* next = sym->next;
|
|
freeSymbol(sym);
|
|
sym = next;
|
|
}
|
|
}
|
|
|
|
static void
|
|
constantFree(NCConstant* con)
|
|
{
|
|
switch(con->nctype) {
|
|
case NC_COMPOUND:
|
|
/* do nothing; ReclaimDatalists below will take care of the datalist */
|
|
break;
|
|
case NC_STRING:
|
|
if(con->value.stringv.len > 0 && con->value.stringv.stringv != NULL)
|
|
efree(con->value.stringv.stringv);
|
|
break;
|
|
case NC_OPAQUE:
|
|
if(con->value.opaquev.len > 0 && con->value.opaquev.stringv != NULL)
|
|
efree(con->value.opaquev.stringv);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
reclaimDatalists(void)
|
|
{
|
|
Datalist* list;
|
|
NCConstant* con;
|
|
/* Step 1: free up the constant content of each datalist*/
|
|
for(list=alldatalists;list != NULL; list = list->next) {
|
|
if(list->data != NULL) { /* avoid multiple free attempts*/
|
|
int i;
|
|
for(i=0,con=list->data;i<list->length;i++,con++)
|
|
constantFree(con);
|
|
list->data = NULL;
|
|
}
|
|
}
|
|
/* Step 2: free up the datalist itself*/
|
|
for(list=alldatalists;list != NULL;) {
|
|
Datalist* current = list;
|
|
list = list->next;
|
|
efree(current);
|
|
}
|
|
}
|
|
|
|
void
|
|
cleanup()
|
|
{
|
|
reclaimDatalists();
|
|
reclaimSymbols();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* compute the total n-dimensional size as 1 long array;
|
|
if stop == 0, then stop = dimset->ndims.
|
|
*/
|
|
size_t
|
|
crossproduct(Dimset* dimset, int start, int stop)
|
|
{
|
|
size_t totalsize = 1;
|
|
int i;
|
|
for(i=start;i<stop;i++) {
|
|
totalsize = totalsize * dimset->dimsyms[i]->dim.declsize;
|
|
}
|
|
return totalsize;
|
|
}
|
|
|
|
/* Do the "complement" of crossproduct;
|
|
compute the total n-dimensional size of an array
|
|
starting at 0 thru the 'last' array index.
|
|
stop if we encounter an unlimited dimension
|
|
*/
|
|
size_t
|
|
prefixarraylength(Dimset* dimset, int last)
|
|
{
|
|
return crossproduct(dimset,0,last+1);
|
|
}
|
|
|
|
|
|
|
|
#ifdef USE_NETCDF4
|
|
extern int H5Eprint1(FILE * stream);
|
|
#endif
|
|
|
|
void
|
|
check_err(const int stat, const int line, const char* file)
|
|
{
|
|
check_err2(stat,-1,line,file);
|
|
}
|
|
|
|
void check_err2(const int stat, const int cdlline, const int line, const char* file) {
|
|
if (stat != NC_NOERR) {
|
|
if(cdlline >= 0)
|
|
fprintf(stderr, "ncgen: cdl line %d; %s\n", cdlline, nc_strerror(stat));
|
|
else
|
|
fprintf(stderr, "ncgen: %s\n", nc_strerror(stat));
|
|
fprintf(stderr, "\t(%s:%d)\n", file,line);
|
|
#ifdef USE_NETCDF4
|
|
H5Eprint1(stderr);
|
|
#endif
|
|
fflush(stderr);
|
|
finalize_netcdf(1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Find the index of the first unlimited
|
|
dimension at or after 'start'.
|
|
If no unlimited exists, return |dimset|
|
|
*/
|
|
int
|
|
findunlimited(Dimset* dimset, int start)
|
|
{
|
|
for(;start<dimset->ndims;start++) {
|
|
if(dimset->dimsyms[start]->dim.isunlimited)
|
|
return start;
|
|
}
|
|
return dimset->ndims;
|
|
}
|
|
|
|
/**
|
|
Find the index of the last unlimited
|
|
dimension.
|
|
If no unlimited exists, return |dimset|
|
|
*/
|
|
int
|
|
findlastunlimited(Dimset* dimset)
|
|
{
|
|
int i;
|
|
for(i=dimset->ndims-1;i>=0;i--) {
|
|
if(dimset->dimsyms[i]->dim.isunlimited)
|
|
return i;
|
|
}
|
|
return dimset->ndims;
|
|
}
|
|
|
|
/**
|
|
Count the number of unlimited dimensions.
|
|
*/
|
|
int
|
|
countunlimited(Dimset* dimset)
|
|
{
|
|
int i, count;
|
|
for(count=0,i=dimset->ndims-1;i>=0;i--) {
|
|
if(dimset->dimsyms[i]->dim.isunlimited)
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/* Return standard format string */
|
|
const char *
|
|
kind_string(int kind)
|
|
{
|
|
switch (kind) {
|
|
case 1: return "classic";
|
|
case 2: return "64-bit offset";
|
|
case 3: return "netCDF-4";
|
|
case 4: return "netCDF-4 classic model";
|
|
default:
|
|
derror("Unknown format index: %d\n",kind);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef USE_NETCDF4i
|
|
nt
|
|
getrootid(int grpid)
|
|
{
|
|
int current = grpid;
|
|
int parent = current;
|
|
/* see if root id */
|
|
for(;;) {
|
|
int stat = nc_inq_grp_parent(current,&parent);
|
|
if(stat) break;
|
|
current = parent;
|
|
}
|
|
return current;
|
|
}
|
|
#endif
|