netcdf-c/libdap4/d4meta.c
Dennis Heimbigner 3db4f013bf Primary change: add dap4 support
Specific changes:
1. Add dap4 code: libdap4 and dap4_test.
   Note that until the d4ts server problem is solved, dap4 is turned off.
2. Modify various files to support dap4 flags:
	configure.ac, Makefile.am, CMakeLists.txt, etc.
3. Add nc_test/test_common.sh. This centralizes
   the handling of the locations of various
   things in the build tree: e.g. where is
   ncgen.exe located. See nc_test/test_common.sh
   for details.
4. Modify .sh files to use test_common.sh
5. Obsolete separate oc2 by moving it to be part of
   netcdf-c. This means replacing code with netcdf-c
   equivalents.
5. Add --with-testserver to configure.ac to allow
   override of the servers to be used for --enable-dap-remote-tests.
6. There were multiple versions of nctypealignment code. Try to
   centralize in libdispatch/doffset.c and include/ncoffsets.h
7. Add a unit test for the ncuri code because of its complexity.
8. Move the findserver code out of libdispatch and into
   a separate, self contained program in ncdap_test and dap4_test.
9. Move the dispatch header files (nc{3,4}dispatch.h) to
   .../include because they are now shared by modules.
10. Revamp the handling of TOPSRCDIR and TOPBUILDDIR for shell scripts.
11. Make use of MREMAP if available
12. Misc. minor changes e.g.
	- #include <config.h> -> #include "config.h"
	- Add some no-install headers to /include
	- extern -> EXTERNL and vice versa as needed
	- misc header cleanup
	- clean up checking for misc. unix vs microsoft functions
13. Change copyright decls in some files to point to LICENSE file.
14. Add notes to RELEASENOTES.md
2017-03-08 17:01:10 -07:00

1113 lines
31 KiB
C

/*********************************************************************
* Copyright 2016, UCAR/Unidata
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
*********************************************************************/
#include "d4includes.h"
#include <stdarg.h>
#include "nc4internal.h"
#include "ncoffsets.h"
#include "ezxml.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 buildBytestringType(NCD4meta* builder);
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* basetype, NClist* values, void** memoryp);
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);
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, size_t* dimsizes);
static void reclaimNode(NCD4node* node);
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);
/***************************************************/
/* 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->allnodes);i++) {
NCD4node* n = (NCD4node*)nclistget(metadata->allnodes,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);
}
/* Topo sort the set of all nodes */
NCD4_toposort(metadata);
markfixedsize(metadata);
markdapsize(metadata);
/* Process the metadata state */
ret = build(metadata,metadata->root);
/* 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(size_t rawsize, void* rawdata)
{
NCD4meta* meta = (NCD4meta*)calloc(1,sizeof(NCD4meta));
if(meta == NULL) return NULL;
meta->allnodes = nclistnew();
meta->serial.rawsize = rawsize;
meta->serial.rawdata = rawdata;
#ifdef D4DEBUG
meta->debuglevel = 1;
#endif
return meta;
}
void
NCD4_setdebuglevel(NCD4meta* meta, int debuglevel)
{
meta->debuglevel = debuglevel;
}
void
NCD4_reclaimMeta(NCD4meta* dataset)
{
int i;
if(dataset == NULL) return;
for(i=0;i<nclistlength(dataset->allnodes);i++) {
NCD4node* node = (NCD4node*)nclistget(dataset->allnodes,i);
reclaimNode(node);
}
nullfree(dataset->error.parseerror);
nullfree(dataset->error.message);
nullfree(dataset->error.context);
nullfree(dataset->error.otherinfo);
nullfree(dataset->serial.errdata);
nclistfree(dataset->groupbyid);
#if 0
for(i=0;i<nclistlength(dataset->blobs);i++) {
void* p = nclistget(dataset->blobs,i);
nullfree(p);
}
nclistfree(dataset->blobs);
#endif
nclistfree(dataset->allnodes);
nullfree(dataset->serial.dmr);
free(dataset);
}
static void
reclaimNode(NCD4node* node)
{
if(node == NULL) return;
nullfree(node->name);
nclistfree(node->groups);
nclistfree(node->vars);
nclistfree(node->types);
nclistfree(node->dims);
nclistfree(node->attributes);
nclistfree(node->maps);
nclistfreeall(node->xmlattributes);
nclistfreeall(node->attr.values);
nclistfree(node->en.econsts);
nclistfree(node->group.elements);
nullfree(node->group.dapversion);
nullfree(node->group.dmrversion);
nullfree(node->group.datasetname);
nclistfree(node->group.varbyid);
nullfree(node->nc4.orig.name);
}
/**************************************************/
/* 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 */
/* If _bytestring was required by parser, then create it */
if(builder->_bytestring != NULL && (ret = buildBytestringType(builder)))
goto done;
/* 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)) buildVariable(builder,x);
}
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);
}
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);
}
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;
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;
/* Supress all UCARTAG attributes */
if(strncmp(attr->name,UCARTAG,strlen(UCARTAG)) == 0)
continue;
if(ISGROUP(varorgroup->sort))
varid = NC_GLOBAL;
else
varid = varorgroup->meta.id;
if((ret=compileAttrValues(builder,attr->basetype,attr->attr.values,&memory))) {
nullfree(memory);
FAIL(NC_ERANGE,"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)));
nullfree(memory);
}
done:
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;
size_t 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 */
getDimsizes(field,dimsizes);
NCCHECK((nc_insert_array_compound(group->meta.id, cmpdtype->meta.id,
field->name, field->meta.offset,
field->basetype->meta.id,
rank, dimsizes)));
}
}
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 upto (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);
}
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, size_t* dimsizes)
{
int i;
int rank = nclistlength(var->dims);
for(i=0;i<rank;i++) {
NCD4node* dim = (NCD4node*)nclistget(var->dims,i);
dimsizes[i] = 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* basetype, NClist* values, void** memoryp)
{
int i,ret = NC_NOERR;
int count = nclistlength(values);
unsigned char* memory = NULL;
unsigned char* p;
size_t size;
NCD4node* truebase = NULL;
union ATOMICS converter;
int isenum = 0;
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 = (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);
}
if(memoryp) *memoryp = memory;
done:
return THROW(ret);
}
static void*
copyAtomic(union ATOMICS* converter, nc_type type, size_t len, void* dst)
{
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); break;
converter->s[0] = NULL; /* avoid duplicate free */
}/*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) != 1) return THROW(NC_ERANGE);
break;
case NC_UBYTE:
case NC_USHORT:
case NC_UINT:
case NC_UINT64:
if(sscanf(s,"%llu",&converter->u64) != 1) return THROW(NC_ERANGE);
break;
case NC_FLOAT:
case NC_DOUBLE:
if(sscanf(s,"%lf",&converter->f64) != 1) return THROW(NC_ERANGE);
break;
case NC_CHAR:
converter->i8[0] = s[0];
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;
//field->meta.memsize = size;
/* 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;
}