netcdf-c/libdap2/cdf4.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;
}