netcdf-c/libdap4/d4meta.c

1210 lines
34 KiB
C

/*********************************************************************
* Copyright 2018, UCAR/Unidata
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
*********************************************************************/
#include "d4includes.h"
#include <stdarg.h>
#include <stddef.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, size_t 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);
static int isfilltypecompatible(NCD4node* parenttype, NCD4node* attrtype);
#ifndef FIXEDOPAQUE
static int buildBytestringType(NCD4meta* builder);
#endif
/***************************************************/
/* API */
int
NCD4_metabuild(NCD4meta* metadata, int ncid)
{
int ret = NC_NOERR;
size_t 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);
}
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)
{
size_t i;
int 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)
{
size_t i;
int 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,dim->dim.size,&dim->meta.id)));
}
done:
return THROW(ret);
}
static int
buildEnumeration(NCD4meta* builder, NCD4node* en)
{
size_t i;
int 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)
{
size_t i;
int 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)
{
size_t i;
int 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);
}
if(memory != NULL) {
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)
{
size_t i;
int 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 dimsizes[NC_MAX_VAR_DIMS];
NCD4node* field = (NCD4node*)nclistget(cmpdtype->vars,i);
size_t 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 { /* 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,
(int)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,(int)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 dimids[NC_MAX_VAR_DIMS];
/* Step 1: define the variable */
size_t rank = nclistlength(structvar->dims);
getDimrefs(structvar,dimids);
group = NCD4_groupFor(structvar);
NCCHECK((nc_def_var(group->meta.id,structvar->name,structvar->basetype->meta.id,(int)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 dimids[NC_MAX_VAR_DIMS];
size_t rank = nclistlength(seq->dims);
getDimrefs(seq,dimids);
group = NCD4_groupFor(seq);
NCCHECK((nc_def_var(group->meta.id,seq->name,seq->basetype->meta.id,(int)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, (size_t)var->meta.id);
nclistinsert(group->group.varbyid, (size_t)var->meta.id,var);
}
/* Collect FQN path from var node up to and including
the root group and create an name from it
*/
char*
NCD4_getVarFQN(NCD4node* var, const char* tail)
{
size_t i;
NCD4node* x = NULL;
NClist* path = NULL;
NCbytes* fqn = NULL;
char* result;
path = nclistnew();
for(x=var->container;ISGROUP(x->sort);x=x->container) {
nclistinsert(path,0,x);
}
fqn = ncbytesnew();
for(i=0;i<nclistlength(path);i++) {
NCD4node* grp = (NCD4node*)nclistget(path,i);
char* escaped = backslashEscape(grp->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;
}
/* 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)
{
size_t 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)
{
size_t i;
size_t 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)
{
size_t i;
size_t 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, size_t count)
{
if(mem == NULL) return;
for(size_t 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)
{
size_t i;
int 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;
size_t 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 ignore or fail on type mismatch */
if(container->basetype != basetype) {/* _FillValue/Variable type mismatch */
int compatible = isfilltypecompatible(container->basetype, basetype);
/* Test type compatibility */
if(compatible && FLAGSET(builder->controller->controls.flags,NCF_FILLMISMATCH)) {
/* Force type match */
basetype = (attr->basetype = container->basetype);
} else if(FLAGSET(builder->controller->controls.flags,NCF_FILLMISMATCH_FAIL)) {/* Fail */
FAIL(NC_EBADTYPE,"_FillValue/Variable type mismatch: %s:%s",container->name,attr->name);
} else
goto done; /* ignore */
}
}
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)
{
size_t i;
int 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)
{
size_t 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)
{
size_t 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)
{
size_t 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 gid, 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->dmrmetadata;
if(meta == NULL)
return THROW(NC_EBADID);
/* Locate var node via (grpid,varid) */
grp_id = GROUPIDPART(gid);
group = nclistget(meta->groupbyid, (size_t)grp_id);
if(group == NULL)
return THROW(NC_EBADID);
var = nclistget(group->group.varbyid, (size_t)varid);
if(var == NULL)
return THROW(NC_EBADID);
if(varp) *varp = var;
if(grpp) *grpp = group;
return THROW(ret);
}
static int
isnumerictype(int typesort)
{
switch (typesort) {
case NC_BYTE:
case NC_SHORT:
case NC_INT:
case NC_FLOAT:
case NC_DOUBLE:
case NC_UBYTE:
case NC_USHORT:
case NC_UINT:
case NC_INT64:
case NC_UINT64:
return 1;
default: break;
}
return 0;
}
static int
isfilltypecompatible(NCD4node* parenttype, NCD4node* attrtype)
{
assert(parenttype->sort == NCD4_TYPE && attrtype->sort == NCD4_TYPE);
if(isnumerictype(parenttype->subsort) && isnumerictype(attrtype->subsort))
return 1;
if(parenttype->subsort == NC_ENUM && attrtype->subsort == NC_STRING)
return 1;
return 0;
}