mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-11-27 07:30:33 +08:00
520 lines
16 KiB
C
520 lines
16 KiB
C
/*********************************************************************
|
|
* Copyright 1993, UCAR/Unidata
|
|
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
|
|
|
|
* $Header: /upc/share/CVS/netcdf-3/libncdap4/cdf4.c,v 1.16 2010/05/24 19:59:55 dmh Exp $
|
|
*********************************************************************/
|
|
|
|
#include "ncdap4.h"
|
|
#include "dapalign.h"
|
|
#include "dapdump.h"
|
|
|
|
/* Forward*/
|
|
static NCerror computevarnodes4(NCDAPCOMMON*, NClist*);
|
|
static NCerror computeusertypes4r(NCDAPCOMMON*, CDFnode* tnode, NClist* usertypes);
|
|
static NCerror computetypesizes4(NCDAPCOMMON* drno, CDFnode* tnode);
|
|
static unsigned long cdftotalsize(NClist* dimensions);
|
|
static int getpadding(int offset, int alignment);
|
|
|
|
/* Accumulate useful node sets */
|
|
NCerror
|
|
computecdfnodesets4(NCDAPCOMMON* nccomm)
|
|
{
|
|
unsigned int i;
|
|
NClist* varnodes = nclistnew();
|
|
NClist* allnodes = nccomm->cdf.ddsroot->tree->nodes;
|
|
|
|
if(nccomm->cdf.seqnodes == NULL) nccomm->cdf.seqnodes = nclistnew();
|
|
if(nccomm->cdf.gridnodes == NULL) nccomm->cdf.gridnodes = nclistnew();
|
|
nclistclear(nccomm->cdf.seqnodes);
|
|
nclistclear(nccomm->cdf.gridnodes);
|
|
|
|
/* Separately define the variable nodes */
|
|
computevarnodes4(nccomm,varnodes);
|
|
nclistfree(nccomm->cdf.varnodes);
|
|
nccomm->cdf.varnodes = varnodes;
|
|
|
|
for(i=0;i<nclistlength(allnodes);i++) {
|
|
CDFnode* node = (CDFnode*)nclistget(allnodes,i);
|
|
if(!node->visible) continue;
|
|
switch (node->nctype) {
|
|
case NC_Sequence:
|
|
nclistpush(nccomm->cdf.seqnodes,(ncelem)node);
|
|
break;
|
|
case NC_Grid:
|
|
nclistpush(nccomm->cdf.gridnodes,(ncelem)node);
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
return NC_NOERR;
|
|
}
|
|
|
|
/*
|
|
The variables for a DAP->netcdf-4 translation are defined by
|
|
the top-level objects in the DAP tree.
|
|
Grids and Structures are converted to netcdf-4
|
|
structures. Sequences are converted to vlens.
|
|
One exception about Sequences. If a sequence
|
|
with a single primitive field exists, then that field
|
|
aliases the sequence, although the var node is still
|
|
the sequence node.
|
|
*/
|
|
|
|
static NCerror
|
|
computevarnodes4(NCDAPCOMMON* nccomm, NClist* varnodes)
|
|
{
|
|
unsigned int i;
|
|
NClist* toplevel = nccomm->cdf.ddsroot->subnodes;
|
|
|
|
for(i=0;i<nclistlength(toplevel);i++) {
|
|
CDFnode* var = (CDFnode*)nclistget(toplevel,i);
|
|
/* If this node has a bad name, make it invisible */
|
|
if(dap_badname(var->ncbasename)) {
|
|
char* newname = dap_repairname(var->ncbasename);
|
|
nullfree(var->ncbasename);
|
|
var->ncbasename = newname;
|
|
}
|
|
if(!var->visible) continue;
|
|
if(var->nctype == NC_Sequence && singletonsequence(var)) {
|
|
var->singleton = 1;
|
|
}
|
|
nclistpush(varnodes,(ncelem)var);
|
|
}
|
|
return NC_NOERR;
|
|
}
|
|
|
|
NCerror
|
|
fixgrids4(NCDAPCOMMON* nccomm)
|
|
{
|
|
unsigned int i;
|
|
NClist* topgrids = nclistnew();
|
|
NClist* gridnodes = nccomm->cdf.gridnodes;
|
|
|
|
/* fix the dimensions of all grids (also collect top-level grids)*/
|
|
for(i=0;i<nclistlength(gridnodes);i++) {
|
|
CDFnode* grid = (CDFnode*)nclistget(gridnodes,i);
|
|
if(daptoplevel(grid)) nclistpush(topgrids,(ncelem)grid);
|
|
(void)fixgrid34(nccomm,grid); /* Ignore mal-formed grids */
|
|
}
|
|
|
|
nclistfree(topgrids);
|
|
return NC_NOERR;
|
|
}
|
|
|
|
|
|
NCerror
|
|
computetypenames4(NCDAPCOMMON* nccomm, CDFnode* tnode)
|
|
{
|
|
char* basename;
|
|
char* tname;
|
|
if(tnode->nctype == NC_Primitive) {
|
|
/* Use the field name directly as its type field name */
|
|
ASSERT((tnode->ncbasename != NULL));
|
|
if(tnode->typename != NULL) nullfree(tnode->typename);
|
|
tnode->typename = nulldup(tnode->ncbasename);
|
|
return NC_NOERR;
|
|
}
|
|
/* Otherwise: non-primitive */
|
|
basename = makecdfpathstring3(tnode,nccomm->cdf.separator);
|
|
/* Use special naming for Sequences */
|
|
if(tnode->nctype != NC_Sequence) {
|
|
tname = (char*)malloc(strlen(basename)+strlen("_t")+1);
|
|
MEMCHECK(tname,NC_ENOMEM);
|
|
strcpy(tname,basename);
|
|
strcat(tname,"_t");
|
|
nullfree(tnode->typename);
|
|
tnode->typename = tname;
|
|
} else { /* Sequence */
|
|
tname = (char*)malloc(strlen(basename)+strlen("_record")+1);
|
|
MEMCHECK(tname,NC_ENOMEM);
|
|
strcpy(tname,basename);
|
|
strcat(tname,"_record");
|
|
nullfree(tnode->typename);
|
|
tnode->typename = tname;
|
|
/* Also create the vlen name */
|
|
tname = (char*)malloc(strlen(basename)+strlen("_seq_t")+1);
|
|
MEMCHECK(tname,NC_ENOMEM);
|
|
strcpy(tname,basename);
|
|
strcat(tname,"_seq_t");
|
|
if(tnode->vlenname != NULL) nullfree(tnode->vlenname);
|
|
tnode->vlenname = tname;
|
|
}
|
|
nullfree(basename);
|
|
return NC_NOERR;
|
|
}
|
|
|
|
void
|
|
setvarbasetype(NCDAPCOMMON* nccomm, CDFnode* field)
|
|
{
|
|
switch (field->nctype) {
|
|
case NC_Primitive: {
|
|
CDFnode* seq = field->container;
|
|
if(seq->nctype == NC_Sequence && nclistlength(seq->subnodes) == 1) {
|
|
/* Make the type of the field be the parent*/
|
|
field->typeid = seq->typeid;
|
|
} else
|
|
field->typeid = field->etype;
|
|
} break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
static NCerror
|
|
computetypesizes4(NCDAPCOMMON* nccomm, CDFnode* tnode)
|
|
{
|
|
unsigned int i;
|
|
CDFnode* field;
|
|
size_t offset, alignment, maxalign;
|
|
|
|
if(tnode->typesize.aligned) return NC_NOERR;
|
|
|
|
switch (tnode->nctype) {
|
|
case NC_Grid:
|
|
case NC_Structure:
|
|
offset = 0;
|
|
#ifdef ALIGNCHECK
|
|
fprintf(stderr,"computesize: struct=%s:\n",tnode->name);
|
|
#endif
|
|
/* Alignment of a struct is that of its most stringent field */
|
|
maxalign = 0;
|
|
/* Recurse on each field */
|
|
for(i=0;i<nclistlength(tnode->subnodes);i++) {
|
|
field = (CDFnode*)nclistget(tnode->subnodes,i);
|
|
if(!field->visible) continue;
|
|
computetypesizes4(nccomm,field);
|
|
if(field->typesize.field.alignment > maxalign)
|
|
maxalign = field->typesize.field.alignment;
|
|
alignment = field->typesize.field.alignment;
|
|
offset += getpadding(offset,alignment);
|
|
field->typesize.field.offset = offset;
|
|
offset += field->typesize.field.size;
|
|
#ifdef ALIGNCHECK
|
|
fprintf(stderr,"\tfield=%s\n", field->name);
|
|
fprintf(stderr,"\t\tinstance: %s\n",dumpalign(&field->typesize.instance));
|
|
fprintf(stderr,"\t\tfield: %s\n",dumpalign(&field->typesize.field));
|
|
#endif
|
|
}
|
|
/* The instance size of a struct must be rounded to its alignment */
|
|
tnode->typesize.instance.size = (offset + nccpadding(offset,maxalign));
|
|
tnode->typesize.instance.alignment = maxalign;
|
|
tnode->typesize.instance.offset = 0;
|
|
/* The field size is instance size * # array elements */
|
|
tnode->typesize.field = tnode->typesize.instance;
|
|
tnode->typesize.field.size = tnode->typesize.instance.size
|
|
* cdftotalsize(tnode->array.dimset0);
|
|
#ifdef ALIGNCHECK
|
|
fprintf(stderr,"computesize.final: struct (%s):\n",tnode->name);
|
|
fprintf(stderr,"\tinstance: %s\n",dumpalign(&tnode->typesize.instance));
|
|
fprintf(stderr,"\tfield: %s\n",dumpalign(&tnode->typesize.field));
|
|
fprintf(stderr,"\n");
|
|
#endif
|
|
break;
|
|
|
|
case NC_Sequence:
|
|
/* The Sequence instance is like a structure */
|
|
#ifdef ALIGNCHECK
|
|
fprintf(stderr,"computesize: sequence=%s:\n",tnode->name);
|
|
#endif
|
|
offset = 0;
|
|
maxalign = 0;
|
|
for(i=0;i<nclistlength(tnode->subnodes);i++) {
|
|
field = (CDFnode*)nclistget(tnode->subnodes,i);
|
|
if(!field->visible) continue;
|
|
computetypesizes4(nccomm,field);
|
|
if(field->typesize.field.alignment > maxalign)
|
|
maxalign = field->typesize.field.alignment;
|
|
alignment = field->typesize.field.alignment;
|
|
offset += getpadding(offset,alignment);
|
|
field->typesize.field.offset = offset;
|
|
offset += field->typesize.field.size;
|
|
#ifdef ALIGNCHECK
|
|
fprintf(stderr,"\tfield=%s\n", field->name);
|
|
fprintf(stderr,"\t\tinstance: %s\n",dumpalign(&field->typesize.instance));
|
|
fprintf(stderr,"\t\tfield: %s\n",dumpalign(&field->typesize.field));
|
|
#endif
|
|
}
|
|
/* The instance size of a record must be rounded to its alignment */
|
|
tnode->typesize.instance.size = (offset + nccpadding(offset,maxalign));
|
|
tnode->typesize.instance.alignment = maxalign;
|
|
tnode->typesize.instance.offset = 0;
|
|
/* set field alignment, etc that of nc_vlen_t */
|
|
tnode->typesize.field.alignment = ncctypealignment(NC_VLEN);
|
|
tnode->typesize.field.size = sizeof(nc_vlen_t);
|
|
tnode->typesize.field.offset = 0;
|
|
#ifdef ALIGNCHECK
|
|
fprintf(stderr,"computesize.final: sequence (%s):\n",tnode->name);
|
|
fprintf(stderr,"\tinstance: %s\n",dumpalign(&tnode->typesize.instance));
|
|
fprintf(stderr,"\tfield: %s\n",dumpalign(&tnode->typesize.field));
|
|
fprintf(stderr,"\n");
|
|
#endif
|
|
break;
|
|
|
|
case NC_Primitive:
|
|
tnode->typeid = tnode->etype;
|
|
tnode->typesize.instance.size = nctypesizeof(tnode->etype);
|
|
tnode->typesize.instance.alignment = ncctypealignment(tnode->etype);
|
|
tnode->typesize.instance.offset = 0;
|
|
tnode->typesize.field = tnode->typesize.instance; /* except for... */
|
|
tnode->typesize.field.size = tnode->typesize.instance.size
|
|
* cdftotalsize(tnode->array.dimset0);
|
|
break;
|
|
|
|
default: PANIC1("unexpected nctype: %d",tnode->nctype);
|
|
}
|
|
tnode->typesize.aligned = 1;
|
|
|
|
return NC_NOERR;
|
|
}
|
|
|
|
|
|
static unsigned long
|
|
cdftotalsize(NClist* dimensions)
|
|
{
|
|
unsigned int i;
|
|
unsigned long total = 1;
|
|
if(dimensions != NULL) {
|
|
for(i=0;i<nclistlength(dimensions);i++) {
|
|
CDFnode* dim = (CDFnode*)nclistget(dimensions,i);
|
|
total *= dim->dim.declsize;
|
|
}
|
|
}
|
|
return total;
|
|
}
|
|
|
|
static int
|
|
getpadding(int offset, int alignment)
|
|
{
|
|
int rem = (alignment==0?0:(offset % alignment));
|
|
int pad = (rem==0?0:(alignment - rem));
|
|
return pad;
|
|
}
|
|
|
|
|
|
NCerror
|
|
computeusertypes4(NCDAPCOMMON* nccomm)
|
|
{
|
|
NClist* toplevel = nccomm->cdf.ddsroot->subnodes;
|
|
unsigned int i;
|
|
|
|
nccomm->cdf.usertypes = nclistnew();
|
|
for(i=0;i<nclistlength(toplevel);i++) {
|
|
CDFnode* node = (CDFnode*)nclistget(toplevel,i);
|
|
if(!node->visible) continue;
|
|
computeusertypes4r(nccomm,node,nccomm->cdf.usertypes);
|
|
}
|
|
return NC_NOERR;
|
|
}
|
|
|
|
static NCerror
|
|
computeusertypes4r(NCDAPCOMMON* nccomm, CDFnode* tnode, NClist* usertypes)
|
|
{
|
|
unsigned int i;
|
|
unsigned int fieldcount;
|
|
|
|
switch (tnode->nctype) {
|
|
case NC_Primitive:
|
|
break;
|
|
case NC_Grid:
|
|
case NC_Sequence:
|
|
case NC_Structure:
|
|
fieldcount = 0;
|
|
for(i=0;i<nclistlength(tnode->subnodes);i++) {
|
|
CDFnode* sub = (CDFnode*)nclistget(tnode->subnodes,i);
|
|
if(!sub->visible) continue;
|
|
computeusertypes4r(nccomm,sub,usertypes);
|
|
fieldcount++;
|
|
}
|
|
/* if a struct/seq has no visible fields, then make it invisible */
|
|
if(fieldcount == 0) tnode->visible = 0;
|
|
nclistpush(usertypes,(ncelem)tnode);
|
|
if(tnode->nctype == NC_Sequence && singletonsequence(tnode))
|
|
tnode->singleton = 1;
|
|
break;
|
|
default: break;
|
|
}
|
|
computetypesizes4(nccomm,tnode);
|
|
computetypenames4(nccomm,tnode);
|
|
return NC_NOERR;
|
|
}
|
|
|
|
|
|
/**************************************************/
|
|
/* Test for special sequence field translation:
|
|
namely undimensioned field whose base type is
|
|
primitive and whose parent container has
|
|
only one (visible) field.
|
|
(Actually allow test on field and on the Sequence)
|
|
*/
|
|
|
|
int
|
|
singletonsequence(CDFnode* node)
|
|
{
|
|
CDFnode* thefield;
|
|
if(node == NULL) return 0;
|
|
if(node->nctype == NC_Primitive) return singletonsequence(node->container);
|
|
if(!node->nctype == NC_Sequence) return 0;
|
|
thefield = getsingletonfield(node->subnodes);
|
|
return (thefield == NULL?0:1);
|
|
}
|
|
|
|
/* Return the single primitive visible field from the list;
|
|
return NULL otherwise */
|
|
CDFnode*
|
|
getsingletonfield(NClist* list)
|
|
{
|
|
int i,fieldcount = 0;
|
|
CDFnode* thefield = NULL;
|
|
for(i=0;i<nclistlength(list);i++) {
|
|
CDFnode* field = (CDFnode*)nclistget(list,i);
|
|
if(!field->visible) continue;
|
|
fieldcount++;
|
|
if(field->nctype == NC_Primitive && nclistlength(field->array.dimset0) == 0)
|
|
thefield = field;
|
|
}
|
|
if(fieldcount != 1) thefield = NULL; /* not the right type of field */
|
|
return thefield;
|
|
}
|
|
|
|
|
|
/**************************************************/
|
|
|
|
/* Do not include the final Dataset */
|
|
static void
|
|
reversepath(CDFnode* node, NClist* rpath)
|
|
{
|
|
nclistclear(rpath);
|
|
while(node != NULL && node->nctype != NC_Dataset) {
|
|
nclistpush(rpath,(ncelem)node);
|
|
node = node->container;
|
|
}
|
|
}
|
|
|
|
/* Compute the reverse path name to some length */
|
|
static void
|
|
reversepathstring(unsigned int count, NClist* path, NCbytes* name,
|
|
const char* separator)
|
|
{
|
|
unsigned int i, depth;
|
|
ncbytesclear(name);
|
|
if(count > nclistlength(path)) count = nclistlength(path);
|
|
for(depth=(count-1),i=0;i<count;i++,depth--) {
|
|
CDFnode* node = (CDFnode*)nclistget(path,depth);
|
|
if(i>0) ncbytescat(name,(char*)separator);
|
|
ncbytescat(name,node->ncbasename);
|
|
}
|
|
}
|
|
|
|
NCerror
|
|
shortentypenames4(NCDAPCOMMON* nccomm)
|
|
{
|
|
unsigned int i,j,len,depth;
|
|
NClist* containers = nclistnew();
|
|
NClist* rpath = nclistnew();
|
|
NClist* unique = nclistnew();
|
|
NCbytes* name = ncbytesnew();
|
|
|
|
/* Collect the reverse paths for all the container user types */
|
|
for(i=0;i<nclistlength(nccomm->cdf.usertypes);i++) {
|
|
CDFnode* tnode = (CDFnode*)nclistget(nccomm->cdf.usertypes,i);
|
|
switch (tnode->nctype) {
|
|
case NC_Structure: case NC_Sequence: case NC_Grid:
|
|
nclistpush(containers,(ncelem)tnode);
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
/* repeatedly attempt to create names of increasing length
|
|
until no more conflicts
|
|
*/
|
|
|
|
depth=0;
|
|
do {
|
|
depth++;
|
|
len = nclistlength(containers);
|
|
/* compute revised full name for each container */
|
|
for(i=0;i<len;i++) {
|
|
CDFnode* container = (CDFnode*)nclistget(containers,i);
|
|
reversepath(container,rpath);
|
|
reversepathstring(depth,rpath,name,nccomm->cdf.separator);
|
|
nullfree(container->ncfullname);
|
|
container->ncfullname = ncbytesdup(name);
|
|
}
|
|
/* Now, look for the unique elements */
|
|
nclistclear(unique);
|
|
for(i=0;i<len;i++) {
|
|
CDFnode* orig = (CDFnode*)nclistget(containers,i);
|
|
int match = 0;
|
|
for(j=(i+1);j<len;j++) {
|
|
CDFnode* dup = (CDFnode*)nclistget(containers,j);
|
|
if(strcmp(dup->ncfullname,orig->ncfullname)==0){
|
|
match=1;
|
|
break;
|
|
}
|
|
}
|
|
if(!match) {
|
|
nclistpush(unique,(ncelem)orig);
|
|
}
|
|
}
|
|
/* remove the unique names from further consideration */
|
|
nclistminus(containers,unique);
|
|
} while(nclistlength(containers) > 0);
|
|
/* Now, go thru and rename all the user types */
|
|
for(i=0;i<nclistlength(nccomm->cdf.usertypes);i++) {
|
|
CDFnode* tnode = (CDFnode*)nclistget(nccomm->cdf.usertypes,i);
|
|
switch (tnode->nctype) {
|
|
case NC_Structure: case NC_Grid:
|
|
nullfree(tnode->typename);
|
|
tnode->typename = (char*)malloc(strlen(tnode->ncfullname)
|
|
+ strlen("_t")+1);
|
|
MEMCHECK(tnode->typename,NC_ENOMEM);
|
|
strcpy(tnode->typename,tnode->ncfullname);
|
|
strcat(tnode->typename,"_t");
|
|
break;
|
|
case NC_Sequence:
|
|
nullfree(tnode->typename);
|
|
tnode->typename = (char*)malloc(strlen(tnode->ncfullname)
|
|
+ strlen("_record_t")+1);
|
|
MEMCHECK(tnode->typename,NC_ENOMEM);
|
|
strcpy(tnode->typename,tnode->ncfullname);
|
|
strcat(tnode->typename,"_record_t");
|
|
nullfree(tnode->vlenname);
|
|
tnode->vlenname = (char*)malloc(strlen(tnode->ncfullname)
|
|
+ strlen("_t")+1);
|
|
MEMCHECK(tnode->vlenname,NC_ENOMEM);
|
|
strcpy(tnode->vlenname,tnode->ncfullname);
|
|
strcat(tnode->vlenname,"_t");
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
nclistfree(containers);
|
|
nclistfree(rpath);
|
|
nclistfree(unique);
|
|
ncbytesfree(name);
|
|
return NC_NOERR;
|
|
}
|
|
|
|
/* Define the dimsetplus and dimsetall lists for
|
|
all nodes with dimensions
|
|
*/
|
|
NCerror
|
|
definedimsets4(NCDAPCOMMON* nccomm)
|
|
{
|
|
int i;
|
|
int ncstat = NC_NOERR;
|
|
NClist* allnodes = nccomm->cdf.ddsroot->tree->nodes;
|
|
|
|
for(i=0;i<nclistlength(allnodes);i++) {
|
|
CDFnode* rankednode = (CDFnode*)nclistget(allnodes,i);
|
|
if(rankednode->nctype == NC_Dimension) continue; //ignore
|
|
ASSERT((rankednode->array.dimsetplus == NULL));
|
|
ASSERT((rankednode->array.dimsetall == NULL));
|
|
/* Make dimsetplus and dimsetall == dimset0 */
|
|
rankednode->array.dimsetplus = nclistclone(rankednode->array.dimset0);
|
|
rankednode->array.dimsetall = nclistclone(rankednode->array.dimset0);
|
|
}
|
|
return ncstat;
|
|
}
|