mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-12-27 08:49:16 +08:00
53464e8963
re: https://github.com/Unidata/netcdf-c/issues/2119 H/T to [Egbert Eich](https://github.com/e4t) and [Bas Couwenberg](https://github.com/sebastic) for this PR. It is undesirable to make netcdf be dependent on the availability of libxml2, but it is desirable to allow its use if available. In order to do this, a wrapper API (include/ncxml.h) was constructed that supports either ezxml or libxml2 as the implementation. Additionally, the xml support code was moved to a new directory netcdf-c/libncxml. Primary changes: * Create a new sub-directory named netcdf-c/libncxml to hold all the xml implementation code. * Move ezxml.c and ezxml.h to libncxml * Create a wrapper API -- include/ncxml.h * Create an implementation, ncxml_ezxml.c to support use of ezxml. * Create an implementation, ncxml_xml2.c to support use of libxml2. * Add a check for libxml2 in configure.ac and CMakeLists.txt * Modify libdap to use the wrapper API instead of ezxml directly. Misc. Other Changes: * Change include/netcdf_json.h from built source to be part of the distribution.
1205 lines
34 KiB
C
1205 lines
34 KiB
C
/*********************************************************************
|
|
* Copyright 2018, UCAR/Unidata
|
|
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
|
|
*********************************************************************/
|
|
|
|
#include "d4includes.h"
|
|
#include <stdarg.h>
|
|
#include "nc4internal.h"
|
|
#include "ncoffsets.h"
|
|
|
|
/**
|
|
* Build the netcdf-4 metadata from the NCD4node nodes.
|
|
*/
|
|
|
|
/***************************************************/
|
|
/* Forwards */
|
|
|
|
static char* backslashEscape(const char* s);
|
|
static char* getFieldFQN(NCD4node* field, const char* tail);
|
|
static int build(NCD4meta* builder, NCD4node* root);
|
|
static int buildAtomicVar(NCD4meta* builder, NCD4node* var);
|
|
static int buildAttributes(NCD4meta* builder, NCD4node* varorgroup);
|
|
static int buildCompound(NCD4meta* builder, NCD4node* cmpdtype, NCD4node* group, char* name);
|
|
static int buildDimension(NCD4meta* builder, NCD4node* dim);
|
|
static int buildEnumeration(NCD4meta* builder, NCD4node* en);
|
|
static int buildGroups(NCD4meta*, NCD4node* parent);
|
|
static int buildMaps(NCD4meta* builder, NCD4node* var);
|
|
static int buildMetaData(NCD4meta* builder, NCD4node* var);
|
|
static int buildOpaque(NCD4meta* builder, NCD4node* op);
|
|
static int buildSequence(NCD4meta* builder, NCD4node* seq);
|
|
static int buildStructure(NCD4meta* builder, NCD4node* structvar);
|
|
static int buildStructureType(NCD4meta* builder, NCD4node* structtype);
|
|
static int buildVariable(NCD4meta* builder, NCD4node* var);
|
|
static int buildVlenType(NCD4meta* builder, NCD4node* seqtype);
|
|
static int compileAttrValues(NCD4meta* builder, NCD4node* attr, void** memoryp, NClist* blobs);
|
|
static void computeOffsets(NCD4meta* builder, NCD4node* cmpd);
|
|
static int convertString(union ATOMICS* converter, NCD4node* type, const char* s);
|
|
static void* copyAtomic(union ATOMICS* converter, nc_type type, size_t len, void* dst, NClist* blobs);
|
|
static int decodeEconst(NCD4meta* builder, NCD4node* enumtype, const char* nameorval, union ATOMICS* converter);
|
|
static int downConvert(union ATOMICS* converter, NCD4node* type);
|
|
static void freeStringMemory(char** mem, int count);
|
|
static size_t getDimrefs(NCD4node* var, int* dimids);
|
|
static size_t getDimsizes(NCD4node* var, int* dimsizes);
|
|
static d4size_t getpadding(d4size_t offset, size_t alignment);
|
|
static int markdapsize(NCD4meta* meta);
|
|
static int markfixedsize(NCD4meta* meta);
|
|
static void savegroupbyid(NCD4meta*,NCD4node* group);
|
|
static void savevarbyid(NCD4node* group, NCD4node* var);
|
|
#ifndef FIXEDOPAQUE
|
|
static int buildBytestringType(NCD4meta* builder);
|
|
#endif
|
|
|
|
/***************************************************/
|
|
/* API */
|
|
|
|
int
|
|
NCD4_metabuild(NCD4meta* metadata, int ncid)
|
|
{
|
|
int ret = NC_NOERR;
|
|
int i;
|
|
|
|
metadata->ncid = ncid;
|
|
metadata->root->meta.id = ncid;
|
|
|
|
/* Fix up the atomic types */
|
|
for(i=0;i<nclistlength(metadata->atomictypes);i++) {
|
|
NCD4node* n = (NCD4node*)nclistget(metadata->atomictypes,i);
|
|
if(n->sort != NCD4_TYPE) continue;
|
|
if(n->subsort > NC_MAX_ATOMIC_TYPE) continue;
|
|
n->meta.id = n->subsort;
|
|
n->meta.isfixedsize = (n->subsort == NC_STRING ? 0 : 1);
|
|
if(n->subsort <= NC_STRING)
|
|
n->meta.dapsize = NCD4_typesize(n->subsort);
|
|
n->container = metadata->root;
|
|
}
|
|
|
|
/* Topo sort the set of all nodes */
|
|
NCD4_toposort(metadata);
|
|
markfixedsize(metadata);
|
|
markdapsize(metadata);
|
|
/* Process the metadata state */
|
|
if((ret = build(metadata,metadata->root))) goto done;
|
|
/* Done with the metadata*/
|
|
if((ret=nc_enddef(metadata->ncid)))
|
|
goto done;
|
|
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
|
|
|
|
/* Create an empty NCD4meta object for
|
|
use in subsequent calls
|
|
(is the the right src file to hold this?)
|
|
*/
|
|
|
|
NCD4meta*
|
|
NCD4_newmeta(NCD4INFO* info)
|
|
{
|
|
NCD4meta* meta = (NCD4meta*)calloc(1,sizeof(NCD4meta));
|
|
if(meta == NULL) return NULL;
|
|
meta->allnodes = nclistnew();
|
|
#ifdef D4DEBUG
|
|
meta->debuglevel = 1;
|
|
#endif
|
|
meta->controller = info;
|
|
meta->ncid = info->substrate.nc4id; /* Transfer netcdf ncid */
|
|
return meta;
|
|
}
|
|
|
|
/* Attach raw data to metadata */
|
|
void
|
|
NCD4_attachraw(NCD4meta* meta, size_t rawsize, void* rawdata)
|
|
{
|
|
assert(meta != NULL);
|
|
NCD4_resetSerial(&meta->serial,rawsize,rawdata);
|
|
}
|
|
|
|
void
|
|
NCD4_setdebuglevel(NCD4meta* meta, int debuglevel)
|
|
{
|
|
meta->debuglevel = debuglevel;
|
|
}
|
|
|
|
void
|
|
NCD4_reclaimMeta(NCD4meta* dataset)
|
|
{
|
|
int i;
|
|
if(dataset == NULL) return;
|
|
NCD4_resetMeta(dataset);
|
|
|
|
for(i=0;i<nclistlength(dataset->allnodes);i++) {
|
|
NCD4node* node = (NCD4node*)nclistget(dataset->allnodes,i);
|
|
reclaimNode(node);
|
|
}
|
|
nclistfree(dataset->allnodes);
|
|
nclistfree(dataset->groupbyid);
|
|
nclistfree(dataset->atomictypes);
|
|
free(dataset);
|
|
}
|
|
|
|
void
|
|
NCD4_resetMeta(NCD4meta* dataset)
|
|
{
|
|
if(dataset == NULL) return;
|
|
nullfree(dataset->error.parseerror); dataset->error.parseerror = NULL;
|
|
nullfree(dataset->error.message); dataset->error.message = NULL;
|
|
nullfree(dataset->error.context); dataset->error.context = NULL;
|
|
nullfree(dataset->error.otherinfo); dataset->error.otherinfo = NULL;
|
|
NCD4_resetSerial(&dataset->serial,0,NULL);
|
|
#if 0
|
|
for(i=0;i<nclistlength(dataset->blobs);i++) {
|
|
void* p = nclistget(dataset->blobs,i);
|
|
nullfree(p);
|
|
}
|
|
nclistfree(dataset->blobs);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
reclaimNode(NCD4node* node)
|
|
{
|
|
if(node == NULL) return;
|
|
nullfree(node->name); node->name = NULL;
|
|
nclistfree(node->groups); node->groups = NULL;
|
|
nclistfree(node->vars); node->vars = NULL;
|
|
nclistfree(node->types); node->types = NULL;
|
|
nclistfree(node->dims); node->dims = NULL;
|
|
nclistfree(node->attributes); node->attributes = NULL;
|
|
nclistfreeall(node->mapnames); node->mapnames = NULL;
|
|
nclistfree(node->maps); node->maps = NULL;
|
|
nclistfreeall(node->xmlattributes); node->xmlattributes = NULL;
|
|
nclistfreeall(node->attr.values); node->attr.values = NULL;
|
|
nclistfree(node->en.econsts); node->en.econsts = NULL;
|
|
nclistfree(node->group.elements); node->group.elements = NULL;
|
|
nullfree(node->group.dapversion); node->group.dapversion = NULL;
|
|
nullfree(node->group.dmrversion); node->group.dmrversion = NULL;
|
|
nullfree(node->group.datasetname); node->group.datasetname = NULL;
|
|
nclistfree(node->group.varbyid); node->group.varbyid = NULL;
|
|
nullfree(node->nc4.orig.name); node->nc4.orig.name = NULL;
|
|
nullfree(node);
|
|
}
|
|
|
|
/**************************************************/
|
|
|
|
/* Recursively walk the tree to create the metadata */
|
|
static int
|
|
build(NCD4meta* builder, NCD4node* root)
|
|
{
|
|
int i,ret = NC_NOERR;
|
|
size_t len = nclistlength(builder->allnodes);
|
|
|
|
/* Tag the root group */
|
|
savegroupbyid(builder,root);
|
|
|
|
/* Compute the sizes for all type objects. Will of necessity
|
|
compute the offsets for compound types as well
|
|
*/
|
|
for(i=0;i<len;i++) {/* Walk in postfix order */
|
|
NCD4node* x = (NCD4node*)nclistget(builder->allnodes,i);
|
|
if(x->sort != NCD4_TYPE) continue;
|
|
switch (x->subsort) {
|
|
case NC_OPAQUE:
|
|
case NC_ENUM:
|
|
case NC_SEQ:
|
|
default: /* Atomic */
|
|
x->meta.memsize = NCD4_computeTypeSize(builder,x);
|
|
x->meta.alignment = x->meta.memsize; /* Same for these cases */
|
|
break;
|
|
case NC_STRUCT:
|
|
/* We need to compute the field offsets in order to compute the struct size */
|
|
computeOffsets(builder,x);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Start by defining group tree separately so we can maintain
|
|
order */
|
|
if((ret=buildGroups(builder,root))) goto done;
|
|
|
|
/* Now, walks selected other nodes to define the netcdf-4 substrate metadata */
|
|
|
|
/* Walk and define the dimensions */
|
|
for(i=0;i<len;i++) {/* Walk in postfix order */
|
|
NCD4node* x = (NCD4node*)nclistget(builder->allnodes,i);
|
|
if(x->sort != NCD4_DIM) continue;
|
|
if((ret=buildDimension(builder,x)))
|
|
goto done;
|
|
}
|
|
|
|
/* Walk and define the enums */
|
|
for(i=0;i<len;i++) {/* Walk in postfix order */
|
|
NCD4node* x = (NCD4node*)nclistget(builder->allnodes,i);
|
|
if(x->sort != NCD4_TYPE) continue;
|
|
if(x->subsort != NC_ENUM) continue;
|
|
if((ret=buildEnumeration(builder,x)))
|
|
goto done;
|
|
}
|
|
|
|
/* Walk and define the opaques */
|
|
#ifndef FIXEDOPAQUE
|
|
/* If _bytestring was required by parser, then create it */
|
|
if(builder->_bytestring != NULL && (ret = buildBytestringType(builder)))
|
|
goto done;
|
|
#endif
|
|
/* Create other opaque types */
|
|
for(i=0;i<len;i++) {/* Walk in postfix order */
|
|
NCD4node* x = (NCD4node*)nclistget(builder->allnodes,i);
|
|
if(x->sort != NCD4_TYPE) continue;
|
|
if(x->subsort != NC_OPAQUE) continue;
|
|
if(x->opaque.size > 0 && (ret=buildOpaque(builder,x)))
|
|
goto done;
|
|
}
|
|
|
|
/* Walk and define the compounds and sequences */
|
|
for(i=0;i<len;i++) {/* Walk in postfix order */
|
|
NCD4node* x = (NCD4node*)nclistget(builder->allnodes,i);
|
|
if(x->sort != NCD4_TYPE) continue;
|
|
switch(x->subsort) {
|
|
case NC_STRUCT:
|
|
if((ret=buildStructureType(builder,x)))
|
|
goto done;
|
|
break;
|
|
case NC_SEQ:
|
|
if((ret=buildVlenType(builder,x)))
|
|
goto done;
|
|
break;
|
|
default: /* ignore */ break;
|
|
}
|
|
}
|
|
|
|
/* Compute the type size for all type */
|
|
for(i=0;i<len;i++) {/* Walk in postfix order */
|
|
NCD4node* x = (NCD4node*)nclistget(builder->allnodes,i);
|
|
if(x->sort != NCD4_TYPE) continue;;
|
|
NCD4_computeTypeSize(builder,x);
|
|
}
|
|
|
|
/* Finally, define the top-level variables */
|
|
for(i=0;i<len;i++) {
|
|
NCD4node* x = (NCD4node*)nclistget(builder->allnodes,i);
|
|
if(ISVAR(x->sort) && ISTOPLEVEL(x)) {
|
|
if((ret=buildVariable(builder,x))) goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
|
|
static int
|
|
buildGroups(NCD4meta* builder, NCD4node* parent)
|
|
{
|
|
int i,ret=NC_NOERR;
|
|
#ifdef D4DEBUG
|
|
fprintf(stderr,"build group: %s\n",parent->name);
|
|
#endif
|
|
/* Define any group level attributes */
|
|
if((ret = buildAttributes(builder,parent))) goto done;
|
|
|
|
for(i=0;i<nclistlength(parent->groups);i++) {
|
|
NCD4node* g = (NCD4node*)nclistget(parent->groups,i);
|
|
if(g->group.isdataset) {
|
|
g->meta.id = builder->ncid;
|
|
} else {
|
|
NCCHECK((nc_def_grp(parent->meta.id,g->name,&g->meta.id)));
|
|
savegroupbyid(builder,g);
|
|
}
|
|
if((ret=buildGroups(builder,g))) goto done; /* recurse */
|
|
}
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
|
|
static int
|
|
buildDimension(NCD4meta* builder, NCD4node* dim)
|
|
{
|
|
int ret = NC_NOERR;
|
|
NCD4node* group = NCD4_groupFor(dim);
|
|
if(dim->dim.isunlimited) {
|
|
NCCHECK((nc_def_dim(group->meta.id,dim->name,NC_UNLIMITED,&dim->meta.id)));
|
|
} else {
|
|
NCCHECK((nc_def_dim(group->meta.id,dim->name,(size_t)dim->dim.size,&dim->meta.id)));
|
|
}
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
|
|
static int
|
|
buildEnumeration(NCD4meta* builder, NCD4node* en)
|
|
{
|
|
int i,ret = NC_NOERR;
|
|
NCD4node* group = NCD4_groupFor(en);
|
|
NCCHECK((nc_def_enum(group->meta.id,en->basetype->meta.id,en->name,&en->meta.id)));
|
|
for(i=0;i<nclistlength(en->en.econsts);i++) {
|
|
NCD4node* ec = (NCD4node*)nclistget(en->en.econsts,i);
|
|
NCCHECK((nc_insert_enum(group->meta.id, en->meta.id, ec->name, ec->en.ecvalue.i8)));
|
|
}
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
|
|
static int
|
|
buildOpaque(NCD4meta* builder, NCD4node* op)
|
|
{
|
|
int ret = NC_NOERR;
|
|
NCD4node* group = NCD4_groupFor(op);
|
|
char* name = op->name;
|
|
|
|
assert(op->opaque.size > 0);
|
|
/* Two cases, with and without UCARTAGORIGTYPE */
|
|
if(op->nc4.orig.name != NULL) {
|
|
name = op->nc4.orig.name;
|
|
group = op->nc4.orig.group;
|
|
}
|
|
NCCHECK((nc_def_opaque(group->meta.id,op->opaque.size,name,&op->meta.id)));
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
|
|
#ifndef FIXEDOPAQUE
|
|
static int
|
|
buildBytestringType(NCD4meta* builder)
|
|
{
|
|
int ret = NC_NOERR;
|
|
NCD4node* bstring = builder->_bytestring;
|
|
|
|
assert(bstring != NULL); /* Will fail if we need bytestring and it was not created in d4parse*/
|
|
|
|
/* Define once */
|
|
if(bstring->meta.id > 0) goto done;
|
|
/* create in root as ubyte(*) vlen named "_bytestring" */
|
|
NCCHECK((nc_def_vlen(builder->root->meta.id,bstring->name,NC_UBYTE,&bstring->meta.id)));
|
|
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
buildVariable(NCD4meta* builder, NCD4node* var)
|
|
{
|
|
int ret = NC_NOERR;
|
|
|
|
switch (var->subsort) {
|
|
default:
|
|
if((ret = buildAtomicVar(builder,var))) goto done;
|
|
break;
|
|
case NC_STRUCT:
|
|
if((ret = buildStructure(builder,var))) goto done;
|
|
break;
|
|
case NC_SEQ:
|
|
if((ret = buildSequence(builder,var))) goto done;
|
|
break;
|
|
}
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
|
|
static int
|
|
buildMetaData(NCD4meta* builder, NCD4node* var)
|
|
{
|
|
int ret = NC_NOERR;
|
|
if((ret = buildAttributes(builder,var))) goto done;
|
|
if((ret = buildMaps(builder,var))) goto done;
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
|
|
static int
|
|
buildMaps(NCD4meta* builder, NCD4node* var)
|
|
{
|
|
int i,ret = NC_NOERR;
|
|
size_t count = nclistlength(var->maps);
|
|
char** memory = NULL;
|
|
char** p;
|
|
NCD4node* group;
|
|
|
|
if(count == 0) goto done;
|
|
|
|
/* Add an attribute to the parent variable
|
|
listing fqn's of all specified variables in map order*/
|
|
memory = (char**)d4alloc(count*sizeof(char*));
|
|
if(memory == NULL) {ret=NC_ENOMEM; goto done;}
|
|
p = memory;
|
|
for(i=0;i<count;i++) {
|
|
NCD4node* mapref = (NCD4node*)nclistget(var->maps,i);
|
|
char* fqn = NCD4_makeFQN(mapref);
|
|
*p++ = fqn;
|
|
}
|
|
/* Make map info visible in the netcdf-4 file */
|
|
group = NCD4_groupFor(var);
|
|
NCCHECK((nc_put_att(group->meta.id,var->meta.id,NC4TAGMAPS,NC_STRING,count,memory)));
|
|
done:
|
|
if(memory != NULL)
|
|
freeStringMemory(memory,count);
|
|
return THROW(ret);
|
|
}
|
|
|
|
static int
|
|
buildAttributes(NCD4meta* builder, NCD4node* varorgroup)
|
|
{
|
|
int i,ret = NC_NOERR;
|
|
NClist* blobs = NULL;
|
|
|
|
for(i=0;i<nclistlength(varorgroup->attributes);i++) {
|
|
NCD4node* attr = nclistget(varorgroup->attributes,i);
|
|
void* memory = NULL;
|
|
size_t count = nclistlength(attr->attr.values);
|
|
NCD4node* group;
|
|
int varid;
|
|
|
|
/* Suppress all UCARTAG attributes */
|
|
if(strncmp(attr->name,UCARTAG,strlen(UCARTAG)) == 0)
|
|
continue;
|
|
|
|
/* Suppress all reserved attributes */
|
|
if(NCD4_lookupreserved(attr->name) != NULL)
|
|
continue;
|
|
|
|
if(ISGROUP(varorgroup->sort))
|
|
varid = NC_GLOBAL;
|
|
else
|
|
varid = varorgroup->meta.id;
|
|
blobs = nclistnew();
|
|
if((ret=compileAttrValues(builder,attr,&memory,blobs))) {
|
|
nullfree(memory);
|
|
FAIL(ret,"Malformed attribute value(s) for: %s",attr->name);
|
|
}
|
|
group = NCD4_groupFor(varorgroup);
|
|
NCCHECK((nc_put_att(group->meta.id,varid,attr->name,attr->basetype->meta.id,count,memory)));
|
|
nclistfreeall(blobs); blobs = NULL;
|
|
nullfree(memory);
|
|
}
|
|
done:
|
|
nclistfreeall(blobs);
|
|
return THROW(ret);
|
|
}
|
|
|
|
static int
|
|
buildStructureType(NCD4meta* builder, NCD4node* structtype)
|
|
{
|
|
int tid,ret = NC_NOERR;
|
|
NCD4node* group = NULL;
|
|
char* name = NULL;
|
|
|
|
group = NCD4_groupFor(structtype); /* default */
|
|
|
|
/* Figure out the type name and containing group */
|
|
if(structtype->nc4.orig.name != NULL) {
|
|
name = strdup(structtype->nc4.orig.name);
|
|
group = structtype->nc4.orig.group;
|
|
} else {
|
|
name = getFieldFQN(structtype,"_t");
|
|
}
|
|
|
|
/* Step 2: See if already defined */
|
|
if(nc_inq_typeid(group->meta.id,name,&tid) == NC_NOERR) {/* Already exists */
|
|
FAIL(NC_ENAMEINUSE,"Inferred type name conflict",name);
|
|
}
|
|
|
|
/* Since netcdf does not support forward references,
|
|
we presume all field types are defined */
|
|
if((ret=buildCompound(builder,structtype,group,name))) goto done;
|
|
|
|
done:
|
|
nullfree(name);
|
|
return THROW(ret);
|
|
}
|
|
|
|
static int
|
|
buildVlenType(NCD4meta* builder, NCD4node* vlentype)
|
|
{
|
|
int ret = NC_NOERR;
|
|
NCD4node* group;
|
|
NCD4node* basetype;
|
|
nc_type tid = NC_NAT;
|
|
char* name = NULL;
|
|
|
|
group = NCD4_groupFor(vlentype);
|
|
|
|
/* Figure out the type name and containing group */
|
|
if(vlentype->nc4.orig.name != NULL) {
|
|
name = strdup(vlentype->nc4.orig.name);
|
|
group = vlentype->nc4.orig.group;
|
|
} else {
|
|
name = getFieldFQN(vlentype,NULL);
|
|
}
|
|
|
|
/* See if already defined */
|
|
if(nc_inq_typeid(group->meta.id,name,&tid) == NC_NOERR) {/* Already exists */
|
|
FAIL(NC_ENAMEINUSE,"Inferred type name conflict",name);
|
|
}
|
|
|
|
/* Get the baseline type */
|
|
basetype = vlentype->basetype;
|
|
/* build the vlen type */
|
|
NCCHECK(nc_def_vlen(group->meta.id, name, basetype->meta.id, &vlentype->meta.id));
|
|
|
|
done:
|
|
nullfree(name);
|
|
return THROW(ret);
|
|
}
|
|
|
|
static int
|
|
buildCompound(NCD4meta* builder, NCD4node* cmpdtype, NCD4node* group, char* name)
|
|
{
|
|
int i,ret = NC_NOERR;
|
|
|
|
/* Step 1: compute field offsets */
|
|
computeOffsets(builder,cmpdtype);
|
|
|
|
/* Step 2: define this node's compound type */
|
|
NCCHECK((nc_def_compound(group->meta.id,(size_t)cmpdtype->meta.memsize,name,&cmpdtype->meta.id)));
|
|
|
|
/* Step 3: add the fields to type */
|
|
for(i=0;i<nclistlength(cmpdtype->vars);i++) {
|
|
int rank;
|
|
int dimsizes[NC_MAX_VAR_DIMS];
|
|
NCD4node* field = (NCD4node*)nclistget(cmpdtype->vars,i);
|
|
rank = nclistlength(field->dims);
|
|
if(rank == 0) { /* scalar */
|
|
NCCHECK((nc_insert_compound(group->meta.id, cmpdtype->meta.id,
|
|
field->name, field->meta.offset,
|
|
field->basetype->meta.id)));
|
|
} else if(rank > 0) { /* array */
|
|
int idimsizes[NC_MAX_VAR_DIMS];
|
|
int j;
|
|
getDimsizes(field,dimsizes);
|
|
/* nc_insert_array_compound: dimsizes arg is not size_t */
|
|
for(j=0;j<rank;j++) idimsizes[j] = (int)dimsizes[j];
|
|
NCCHECK((nc_insert_array_compound(group->meta.id, cmpdtype->meta.id,
|
|
field->name, field->meta.offset,
|
|
field->basetype->meta.id,
|
|
rank, idimsizes)));
|
|
}
|
|
}
|
|
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
|
|
static int
|
|
buildAtomicVar(NCD4meta* builder, NCD4node* var)
|
|
{
|
|
int ret = NC_NOERR;
|
|
size_t rank;
|
|
int dimids[NC_MAX_VAR_DIMS];
|
|
NCD4node* group;
|
|
|
|
group = NCD4_groupFor(var);
|
|
|
|
#ifdef D4DEBUG
|
|
fprintf(stderr,"build var: %s.%s\n",group->name,var->name); fflush(stderr);
|
|
#endif
|
|
|
|
rank = getDimrefs(var,dimids);
|
|
NCCHECK((nc_def_var(group->meta.id,var->name,var->basetype->meta.id,rank,dimids,&var->meta.id)));
|
|
/* Tag the var */
|
|
savevarbyid(group,var);
|
|
|
|
/* Build attributes and map attributes */
|
|
if((ret = buildMetaData(builder,var))) goto done;
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
|
|
static int
|
|
buildStructure(NCD4meta* builder, NCD4node* structvar)
|
|
{
|
|
int ret = NC_NOERR;
|
|
NCD4node* group;
|
|
int rank;
|
|
int dimids[NC_MAX_VAR_DIMS];
|
|
|
|
/* Step 1: define the variable */
|
|
rank = nclistlength(structvar->dims);
|
|
getDimrefs(structvar,dimids);
|
|
group = NCD4_groupFor(structvar);
|
|
NCCHECK((nc_def_var(group->meta.id,structvar->name,structvar->basetype->meta.id,rank,dimids,&structvar->meta.id)));
|
|
/* Tag the var */
|
|
savevarbyid(group,structvar);
|
|
|
|
/* Build attributes and map attributes WRT the variable */
|
|
if((ret = buildMetaData(builder,structvar))) goto done;
|
|
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
|
|
static int
|
|
buildSequence(NCD4meta* builder, NCD4node* seq)
|
|
{
|
|
|
|
int ret = NC_NOERR;
|
|
NCD4node* group;
|
|
int rank;
|
|
int dimids[NC_MAX_VAR_DIMS];
|
|
|
|
rank = nclistlength(seq->dims);
|
|
getDimrefs(seq,dimids);
|
|
group = NCD4_groupFor(seq);
|
|
NCCHECK((nc_def_var(group->meta.id,seq->name,seq->basetype->meta.id,rank,dimids,&seq->meta.id)));
|
|
savevarbyid(group,seq);
|
|
|
|
/* Build attributes and map attributes WRT the variable */
|
|
if((ret = buildMetaData(builder,seq))) goto done;
|
|
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
|
|
/***************************************************/
|
|
/* Utilities */
|
|
|
|
/* Insert a group into the groupbyid for a group */
|
|
static void
|
|
savegroupbyid(NCD4meta* meta, NCD4node* group)
|
|
{
|
|
if(meta->groupbyid == NULL)
|
|
meta->groupbyid = nclistnew();
|
|
nclistsetalloc(meta->groupbyid,GROUPIDPART(group->meta.id));
|
|
nclistinsert(meta->groupbyid,GROUPIDPART(group->meta.id),group);
|
|
}
|
|
|
|
/* Insert a var into the varbyid for a group */
|
|
static void
|
|
savevarbyid(NCD4node* group, NCD4node* var)
|
|
{
|
|
if(group->group.varbyid == NULL)
|
|
group->group.varbyid = nclistnew();
|
|
nclistsetalloc(group->group.varbyid,var->meta.id);
|
|
nclistinsert(group->group.varbyid,var->meta.id,var);
|
|
}
|
|
|
|
/* Collect FQN path from node up to (but not including)
|
|
the first enclosing group and create an name from it
|
|
*/
|
|
static char*
|
|
getFieldFQN(NCD4node* field, const char* tail)
|
|
{
|
|
int i;
|
|
NCD4node* x = NULL;
|
|
NClist* path = NULL;
|
|
NCbytes* fqn = NULL;
|
|
char* result;
|
|
|
|
path = nclistnew();
|
|
for(x=field;!ISGROUP(x->sort);x=x->container) {
|
|
nclistinsert(path,0,x);
|
|
}
|
|
fqn = ncbytesnew();
|
|
for(i=0;i<nclistlength(path);i++) {
|
|
NCD4node* elem = (NCD4node*)nclistget(path,i);
|
|
char* escaped = backslashEscape(elem->name);
|
|
if(escaped == NULL) return NULL;
|
|
if(i > 0) ncbytesappend(fqn,'.');
|
|
ncbytescat(fqn,escaped);
|
|
free(escaped);
|
|
}
|
|
nclistfree(path);
|
|
if(tail != NULL)
|
|
ncbytescat(fqn,tail);
|
|
result = ncbytesextract(fqn);
|
|
ncbytesfree(fqn);
|
|
return result;
|
|
}
|
|
|
|
static size_t
|
|
getDimrefs(NCD4node* var, int* dimids)
|
|
{
|
|
int i;
|
|
int rank = nclistlength(var->dims);
|
|
for(i=0;i<rank;i++) {
|
|
NCD4node* dim = (NCD4node*)nclistget(var->dims,i);
|
|
dimids[i] = dim->meta.id;
|
|
}
|
|
return rank;
|
|
}
|
|
|
|
static size_t
|
|
getDimsizes(NCD4node* var, int* dimsizes)
|
|
{
|
|
int i;
|
|
int rank = nclistlength(var->dims);
|
|
for(i=0;i<rank;i++) {
|
|
NCD4node* dim = (NCD4node*)nclistget(var->dims,i);
|
|
dimsizes[i] = (int)dim->dim.size;
|
|
}
|
|
return rank;
|
|
}
|
|
|
|
/**************************************************/
|
|
/* Utilities */
|
|
|
|
static void
|
|
freeStringMemory(char** mem, int count)
|
|
{
|
|
int i;
|
|
if(mem == NULL) return;
|
|
for(i=0;i<count;i++) {
|
|
char* p = mem[i];
|
|
if(p) free(p);
|
|
}
|
|
free(mem);
|
|
}
|
|
|
|
/**
|
|
Convert a list of attribute value strings
|
|
into a memory chunk capable of being passed
|
|
to nc_put_att().
|
|
*/
|
|
static int
|
|
compileAttrValues(NCD4meta* builder, NCD4node* attr, void** memoryp, NClist* blobs)
|
|
{
|
|
int i,ret = NC_NOERR;
|
|
unsigned char* memory = NULL;
|
|
unsigned char* p;
|
|
size_t size;
|
|
NCD4node* truebase = NULL;
|
|
union ATOMICS converter;
|
|
int isenum = 0;
|
|
NCD4node* container = attr->container;
|
|
NCD4node* basetype = attr->basetype;
|
|
NClist* values = attr->attr.values;
|
|
int count = nclistlength(values);
|
|
|
|
memset((void*)&converter,0,sizeof(converter));
|
|
|
|
/* Deal with _FillValue */
|
|
if(container->sort == NCD4_VAR && strcmp(attr->name,"_FillValue")==0) {
|
|
/* Verify or fix or fail on type match */
|
|
if(container->basetype != basetype) {
|
|
/* _FillValue/Variable type mismatch */
|
|
if(FLAGSET(builder->controller->controls.flags,NCF_FILLMISMATCH)) {
|
|
/* Force type match */
|
|
basetype = (attr->basetype = container->basetype);
|
|
} else {/* Fail */
|
|
FAIL(NC_EBADTYPE,"_FillValue/Variable type mismatch: %s:%s",container->name,attr->name);
|
|
}
|
|
}
|
|
}
|
|
isenum = (basetype->subsort == NC_ENUM);
|
|
truebase = (isenum ? basetype->basetype : basetype);
|
|
if(!ISTYPE(truebase->sort) || (truebase->meta.id > NC_MAX_ATOMIC_TYPE))
|
|
FAIL(NC_EBADTYPE,"Illegal attribute type: %s",basetype->name);
|
|
size = NCD4_typesize(truebase->meta.id);
|
|
if((memory = (unsigned char*)d4alloc(count*size))==NULL)
|
|
return THROW(NC_ENOMEM);
|
|
p = memory;
|
|
for(i=0;i<count;i++) {
|
|
char* s = (char*)nclistget(values,i);
|
|
if(isenum) {
|
|
if((ret=decodeEconst(builder,basetype,s,&converter)))
|
|
FAIL(ret,"Illegal enum const: ",s);
|
|
} else {
|
|
if((ret = convertString(&converter,basetype,s)))
|
|
FAIL(NC_EBADTYPE,"Illegal attribute type: ",basetype->name);
|
|
}
|
|
ret = downConvert(&converter,truebase);
|
|
p = copyAtomic(&converter,truebase->meta.id,NCD4_typesize(truebase->meta.id),p,blobs);
|
|
}
|
|
if(memoryp) *memoryp = memory;
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
|
|
static void*
|
|
copyAtomic(union ATOMICS* converter, nc_type type, size_t len, void* dst, NClist* blobs)
|
|
{
|
|
switch (type) {
|
|
case NC_CHAR: case NC_BYTE: case NC_UBYTE:
|
|
memcpy(dst,&converter->u8[0],len); break;
|
|
case NC_SHORT: case NC_USHORT:
|
|
memcpy(dst,&converter->u16[0],len); break;
|
|
case NC_INT: case NC_UINT:
|
|
memcpy(dst,&converter->u32[0],len); break;
|
|
case NC_INT64: case NC_UINT64:
|
|
memcpy(dst,&converter->u64[0],len); break;
|
|
case NC_FLOAT:
|
|
memcpy(dst,&converter->f32[0],len); break;
|
|
case NC_DOUBLE:
|
|
memcpy(dst,&converter->f64[0],len); break;
|
|
case NC_STRING:
|
|
memcpy(dst,&converter->s[0],len);
|
|
nclistpush(blobs,converter->s[0]);
|
|
converter->s[0] = NULL; /* avoid duplicate free */
|
|
break;
|
|
}/*switch*/
|
|
return (((char*)dst)+len);
|
|
}
|
|
|
|
static int
|
|
convertString(union ATOMICS* converter, NCD4node* type, const char* s)
|
|
{
|
|
switch (type->subsort) {
|
|
case NC_BYTE:
|
|
case NC_SHORT:
|
|
case NC_INT:
|
|
case NC_INT64:
|
|
if(sscanf(s,"%lld",&converter->i64[0]) != 1) return THROW(NC_ERANGE);
|
|
break;
|
|
case NC_UBYTE:
|
|
case NC_USHORT:
|
|
case NC_UINT:
|
|
case NC_UINT64:
|
|
if(sscanf(s,"%llu",&converter->u64[0]) != 1) return THROW(NC_ERANGE);
|
|
break;
|
|
case NC_FLOAT:
|
|
case NC_DOUBLE:
|
|
if(sscanf(s,"%lf",&converter->f64[0]) != 1) return THROW(NC_ERANGE);
|
|
break;
|
|
case NC_CHAR:
|
|
#ifdef WORDS_BIGENDIAN
|
|
converter->i8[7] = s[0];
|
|
#else
|
|
converter->i8[0] = s[0];
|
|
#endif
|
|
break;
|
|
case NC_STRING:
|
|
converter->s[0]= strdup(s);
|
|
break;
|
|
}/*switch*/
|
|
return downConvert(converter,type);
|
|
}
|
|
|
|
static int
|
|
downConvert(union ATOMICS* converter, NCD4node* type)
|
|
{
|
|
d4size_t u64 = converter->u64[0];
|
|
long long i64 = converter->i64[0];
|
|
double f64 = converter->f64[0];
|
|
char* s = converter->s[0];
|
|
switch (type->subsort) {
|
|
case NC_CHAR:
|
|
case NC_BYTE:
|
|
converter->i8[0] = (char)i64;
|
|
break;
|
|
case NC_UBYTE:
|
|
converter->u8[0] = (unsigned char)u64;
|
|
break;
|
|
case NC_SHORT:
|
|
converter->i16[0] = (short)i64;
|
|
break;
|
|
case NC_USHORT:
|
|
converter->u16[0] = (unsigned short)u64;
|
|
break;
|
|
case NC_INT:
|
|
converter->i32[0] = (int)i64;
|
|
break;
|
|
case NC_UINT:
|
|
converter->u32[0] = (unsigned int)u64;
|
|
break;
|
|
case NC_INT64:
|
|
converter->i64[0] = i64;
|
|
break;
|
|
case NC_UINT64:
|
|
converter->u64[0]= u64;
|
|
break;
|
|
case NC_FLOAT:
|
|
converter->f32[0] = (float)f64;
|
|
break;
|
|
case NC_DOUBLE:
|
|
converter->f64[0] = f64;
|
|
break;
|
|
case NC_STRING:
|
|
converter->s[0]= s;
|
|
break;
|
|
}/*switch*/
|
|
return THROW(NC_NOERR);
|
|
}
|
|
|
|
/*
|
|
Given an enum type, and a string representing an econst,
|
|
convert to integer.
|
|
Note: this will work if the econst string is a number or a econst name
|
|
*/
|
|
static int
|
|
decodeEconst(NCD4meta* builder, NCD4node* enumtype, const char* nameorval, union ATOMICS* converter)
|
|
{
|
|
int i,ret=NC_NOERR;
|
|
union ATOMICS number;
|
|
NCD4node* match = NULL;
|
|
|
|
/* First, see if the value is an econst name */
|
|
for(i=0;i<nclistlength(enumtype->en.econsts);i++) {
|
|
NCD4node* ec = (NCD4node*)nclistget(enumtype->en.econsts,i);
|
|
if(strcmp(ec->name,nameorval)==0) {match = ec; break;}
|
|
}
|
|
/* If no match, try to invert as a number to see if there is a matching econst */
|
|
if(!match) {
|
|
/* get the incoming value as number */
|
|
if((ret=convertString(&number,enumtype->basetype,nameorval)))
|
|
goto done;
|
|
for(i=0;i<nclistlength(enumtype->en.econsts);i++) {
|
|
NCD4node* ec = (NCD4node*)nclistget(enumtype->en.econsts,i);
|
|
if(ec->en.ecvalue.u64[0] == number.u64[0]) {match = ec; break;}
|
|
}
|
|
}
|
|
if(match == NULL)
|
|
FAIL(NC_EINVAL,"No enum const matching value: %s",nameorval);
|
|
if(converter) *converter = match->en.ecvalue;
|
|
done:
|
|
return THROW(ret);
|
|
}
|
|
|
|
static char*
|
|
backslashEscape(const char* s)
|
|
{
|
|
const char* p;
|
|
char* q;
|
|
size_t len;
|
|
char* escaped = NULL;
|
|
|
|
len = strlen(s);
|
|
escaped = (char*)d4alloc(1+(2*len)); /* max is everychar is escaped */
|
|
if(escaped == NULL) return NULL;
|
|
for(p=s,q=escaped;*p;p++) {
|
|
char c = *p;
|
|
switch (c) {
|
|
case '\\':
|
|
case '/':
|
|
case '.':
|
|
case '@':
|
|
*q++ = '\\'; *q++ = '\\';
|
|
break;
|
|
default: *q++ = c; break;
|
|
}
|
|
}
|
|
*q = '\0';
|
|
return escaped;
|
|
}
|
|
|
|
/* Tag each compound type as fixed size or not
|
|
Assumes:
|
|
- atomic types defined and marked
|
|
- topo sorted
|
|
*/
|
|
|
|
static int
|
|
markfixedsize(NCD4meta* meta)
|
|
{
|
|
int i,j;
|
|
for(i=0;i<nclistlength(meta->allnodes);i++) {
|
|
int fixed = 1;
|
|
NCD4node* n = (NCD4node*)nclistget(meta->allnodes,i);
|
|
if(n->sort != NCD4_TYPE) continue;
|
|
switch (n->subsort) {
|
|
case NC_STRUCT:
|
|
for(j=0;j<nclistlength(n->vars);j++) {
|
|
NCD4node* field = (NCD4node*)nclistget(n->vars,j);
|
|
if(!field->basetype->meta.isfixedsize) {
|
|
fixed = 0;
|
|
break;
|
|
}
|
|
}
|
|
n->meta.isfixedsize = fixed;
|
|
break;
|
|
case NC_ENUM:
|
|
n->meta.isfixedsize = 1;
|
|
break;
|
|
default: /* leave as is */
|
|
break;
|
|
}
|
|
}
|
|
return NC_NOERR;
|
|
}
|
|
|
|
/* Compute compound type field offsets and compound type total size */
|
|
static void
|
|
computeOffsets(NCD4meta* builder, NCD4node* cmpd)
|
|
{
|
|
int i;
|
|
d4size_t offset = 0;
|
|
d4size_t largestalign = 1;
|
|
d4size_t size = 0;
|
|
|
|
for(i=0;i<nclistlength(cmpd->vars);i++) {
|
|
NCD4node* field = (NCD4node*)nclistget(cmpd->vars,i);
|
|
NCD4node* ftype = field->basetype;
|
|
size_t alignment;
|
|
if(ftype->subsort == NC_STRUCT) {
|
|
/* Recurse */
|
|
computeOffsets(builder, ftype);
|
|
assert(ftype->meta.memsize > 0);
|
|
size=ftype->meta.memsize;
|
|
alignment = ftype->meta.alignment;
|
|
} else {/* Size and alignment will already have been set */
|
|
assert(ftype->meta.memsize > 0);
|
|
alignment = ftype->meta.alignment;
|
|
size=ftype->meta.memsize;
|
|
}
|
|
#if 0
|
|
} else if(ftype->subsort == NC_SEQ) { /* VLEN */
|
|
alignment = nctypealignment(NC_VLEN);
|
|
assert(ftype->meta.memsize > 0); size=ftype->meta.memsize;
|
|
/*size = NCD4_computeTypeSize(builder,ftype);*/
|
|
} else if(ftype->subsort == NC_OPAQUE) {
|
|
/* Either fixed or a vlen */
|
|
assert(ftype->meta.memsize > 0); size=ftype->meta.memsize;
|
|
if(ftype->opaque.size == 0) {/* treat like vlen */
|
|
alignment = nctypealignment(NC_VLEN);
|
|
/*size = NCD4_computeTypeSize(builder,ftype);*/
|
|
} else { /* fixed size */
|
|
alignment = nctypealignment(NC_OPAQUE);
|
|
/*size = NCD4_computeTypeSize(builder,ftype);*/
|
|
}
|
|
} else if(ftype->subsort == NC_ENUM) {
|
|
NCD4node* truetype = ftype->basetype;
|
|
alignment = nctypealignment(truetype->meta.id);
|
|
assert(ftype->meta.memsize > 0); size=ftype->meta.memsize;
|
|
/*size = NCD4_computeTypeSize(builder,truetype);*/
|
|
} else { /* Basically a primitive */
|
|
alignment = nctypealignment(ftype->meta.id);
|
|
assert(ftype->meta.memsize > 0); size=ftype->meta.memsize;
|
|
/*size = NCD4_computeTypeSize(builder,ftype);*/
|
|
}
|
|
#endif
|
|
if(alignment > largestalign)
|
|
largestalign = alignment;
|
|
/* Add possible padding wrt to previous field */
|
|
offset += getpadding(offset,alignment);
|
|
field->meta.offset = offset;
|
|
assert(ftype->meta.memsize > 0);
|
|
size = ftype->meta.memsize;
|
|
#if 0
|
|
field->meta.memsize = size;
|
|
#endif
|
|
/* Now ultiply by the field dimproduct*/
|
|
if(nclistlength(field->dims) > 0) {
|
|
d4size_t count = NCD4_dimproduct(field);
|
|
size = (size * count);
|
|
}
|
|
offset += size;
|
|
}
|
|
/* Compute compound-level info */
|
|
/* A struct alignment is the same as largestalign */
|
|
cmpd->meta.alignment = largestalign;
|
|
offset += (offset % largestalign); /* round up compound size */
|
|
cmpd->meta.memsize = offset;
|
|
}
|
|
|
|
/*
|
|
Compute the in-memory size of an instance of a type.
|
|
Note that nc_inq_type is used, so that C struct field
|
|
alignment is taken into account for compound types.
|
|
The variables total size will be this * dimproduct.
|
|
*/
|
|
size_t
|
|
NCD4_computeTypeSize(NCD4meta* builder, NCD4node* type)
|
|
{
|
|
size_t size = 0;
|
|
|
|
switch (type->sort) {
|
|
case NCD4_TYPE:
|
|
switch (type->subsort) {
|
|
default: size = NCD4_typesize(type->meta.id); break;
|
|
case NC_OPAQUE:
|
|
size = (type->opaque.size == 0 ? sizeof(nc_vlen_t) : type->opaque.size);
|
|
break;
|
|
case NC_ENUM:
|
|
size = NCD4_computeTypeSize(builder,type->basetype);
|
|
break;
|
|
case NC_SEQ:
|
|
size = sizeof(nc_vlen_t);
|
|
break;
|
|
case NC_STRUCT: {
|
|
int ret;
|
|
NCD4node* group = NCD4_groupFor(type);
|
|
if((ret = nc_inq_type(group->meta.id,type->meta.id,/*name*/NULL,&size)))
|
|
return 0;
|
|
}; break;
|
|
}
|
|
break;
|
|
default: break; /* ignore */
|
|
}
|
|
type->meta.memsize = size;
|
|
return size;
|
|
}
|
|
|
|
static d4size_t
|
|
getpadding(d4size_t offset, size_t alignment)
|
|
{
|
|
d4size_t rem = (alignment==0?0:(offset % alignment));
|
|
d4size_t pad = (rem==0?0:(alignment - rem));
|
|
return pad;
|
|
}
|
|
|
|
/* Compute the dap data size for each type; note that this
|
|
is unlikely to be the same as the meta.memsize unless
|
|
the type is atomic and is <= NC_UINT64.
|
|
*/
|
|
|
|
static int
|
|
markdapsize(NCD4meta* meta)
|
|
{
|
|
int i,j;
|
|
for(i=0;i<nclistlength(meta->allnodes);i++) {
|
|
NCD4node* type = (NCD4node*)nclistget(meta->allnodes,i);
|
|
size_t totalsize;
|
|
if(type->sort != NCD4_TYPE) continue;
|
|
switch (type->subsort) {
|
|
case NC_STRUCT:
|
|
totalsize = 0;
|
|
for(j=0;j<nclistlength(type->vars);j++) {
|
|
NCD4node* field = (NCD4node*)nclistget(type->vars,j);
|
|
size_t size = field->basetype->meta.dapsize;
|
|
if(size == 0) {
|
|
totalsize = 0;
|
|
break;
|
|
} else
|
|
totalsize += size;
|
|
}
|
|
type->meta.dapsize = totalsize;
|
|
break;
|
|
case NC_SEQ:
|
|
type->meta.dapsize = 0; /* has no fixed size */
|
|
break;
|
|
case NC_OPAQUE:
|
|
type->meta.dapsize = type->opaque.size;
|
|
break;
|
|
case NC_ENUM:
|
|
type->meta.dapsize = type->basetype->meta.dapsize;
|
|
break;
|
|
case NC_STRING:
|
|
type->meta.dapsize = 0; /* has no fixed size */
|
|
break;
|
|
default:
|
|
assert(type->subsort <= NC_UINT64);
|
|
/* Already assigned */
|
|
break;
|
|
}
|
|
}
|
|
return NC_NOERR;
|
|
}
|
|
|
|
int
|
|
NCD4_findvar(NC* ncp, int ncid, int varid, NCD4node** varp, NCD4node** grpp)
|
|
{
|
|
int ret = NC_NOERR;
|
|
NCD4INFO* info = NULL;
|
|
NCD4meta* meta = NULL;
|
|
NCD4node* var = NULL;
|
|
NCD4node* group = NULL;
|
|
int grp_id;
|
|
|
|
info = getdap(ncp);
|
|
if(info == NULL)
|
|
return THROW(NC_EBADID);
|
|
meta = info->substrate.metadata;
|
|
if(meta == NULL)
|
|
return THROW(NC_EBADID);
|
|
/* Locate var node via (grpid,varid) */
|
|
grp_id = GROUPIDPART(ncid);
|
|
group = nclistget(meta->groupbyid,grp_id);
|
|
if(group == NULL)
|
|
return THROW(NC_EBADID);
|
|
var = nclistget(group->group.varbyid,varid);
|
|
if(var == NULL)
|
|
return THROW(NC_EBADID);
|
|
if(varp) *varp = var;
|
|
if(grpp) *grpp = group;
|
|
return ret;
|
|
}
|