NOTICE: This version of the NSF Unidata web site (archive.unidata.ucar.edu) is no longer being updated.
Current content can be found at unidata.ucar.edu.
To learn about what's going on, see About the Archive Site.
All versions of ncgen from netcdf 4.1 have a significant bug. It manifests itself under the following conditions. 1. You have a cdl file with a variable whose total size is more than 2**18 bytes (~250,000). For example an integer variable with a single dimension larger than 65536 ((2**18)/4), or an integer variable with 2 or more dimensions whose cross product is greater than 65536 (256x256, for example). 2. The cdl data: section contains an assignment to that variable. The symptom is that the data above the first 2**18 bytes is corrupted. I must assume the mea culpa for this problem because our test sets did not include a sufficiently large cdl file to encounter it. The bug has been fixed for the forthcoming version 4.2 (and the nightly snapshot available on March 8, 2012). If you have version 4.2-rc2, then you can do an immediate fix by replacing the file ncgen/generate.c with the attached version. This fix will not work for any version prior to netcdf 4.2-rc2. Thanks to Dave Allured at NOAA for finding this. =Dennis Heimbigner
/********************************************************************* * Copyright 2009, UCAR/Unidata * See netcdf/COPYRIGHT file for copying and redistribution conditions. *********************************************************************/ #include "includes.h" #include "nciter.h" #include "odom.h" #include "offsets.h" /**************************************************/ /* Code for generating data lists*/ /**************************************************/ /* For datalist constant rules: see the rules on the man page */ /* Forward*/ static void generate_array(Symbol*,Bytebuffer*,Datalist*,Generator*,Writer); static void generate_arrayr(Symbol*,Bytebuffer*,Datalist*,Odometer*,int,Datalist*,Generator*); static void generate_primdata(Symbol*, Constant*, Bytebuffer*, Datalist* fillsrc, Generator*); static void generate_fieldarray(Symbol*, Constant*, Dimset*, Bytebuffer*, Datalist* fillsrc, Generator*); /* Mnemonics */ #define VLENLIST1 #define FIELDARRAY 1 /**************************************************/ /* Generator general procedures */ int generator_getstate(Generator* generator ,void** statep) { if(statep) *statep = (void*)generator->state; return 1; } int generator_reset(Generator* generator, void* state) { generator->state = state; return 1; } #ifdef IGNORe static void checkodom(Odometer* odom) { int i; for(i=0;i<odom->rank;i++) { ASSERT(odom->index[i] == odom->start[i]+odom->count[i]); } } #endif /**************************************************/ void generate_attrdata(Symbol* asym, Generator* generator, Writer writer, Bytebuffer* codebuf) { Symbol* basetype = asym->typ.basetype; nc_type typecode = basetype->typ.typecode; if(typecode == NC_CHAR) { gen_charattr(asym->data,codebuf); } else { int uid; size_t count; generator->listbegin(generator,LISTATTR,asym->data->length,codebuf,&uid); for(count=0;count<asym->data->length;count++) { Constant* con = datalistith(asym->data,count); generator->list(generator,LISTATTR,uid,count,codebuf); generate_basetype(asym->typ.basetype,con,codebuf,NULL,generator); } generator->listend(generator,LISTATTR,uid,count,codebuf); } writer(generator,asym,codebuf,0,NULL,NULL); } void generate_vardata(Symbol* vsym, Generator* generator, Writer writer, Bytebuffer* code) { Dimset* dimset = &vsym->typ.dimset; int rank = dimset->ndims; Symbol* basetype = vsym->typ.basetype; Datalist* filler = getfiller(vsym); if(vsym->data == NULL) return; /* give the buffer a running start to be large enough*/ bbSetalloc(code, nciterbuffersize); if(rank == 0) {/*scalar case*/ Constant* c0 = datalistith(vsym->data,0); generate_basetype(basetype,c0,code,filler,generator); writer(generator,vsym,code,0,NULL,NULL); } else {/*rank > 0*/ generate_array(vsym,code,filler,generator,writer); } } static void generate_array(Symbol* vsym, Bytebuffer* code, Datalist* filler, Generator* generator, Writer writer ) { Dimset* dimset = &vsym->typ.dimset; int rank = dimset->ndims; Symbol* basetype = vsym->typ.basetype; nc_type typecode = basetype->typ.typecode; Odometer* odom; nciter_t iter; ASSERT(rank > 0); /* Start by doing the two easy cases */ if(typecode == NC_CHAR) { /* case 1: character typed variable, rank > 0 */ Bytebuffer* charbuf = bbNew(); odom = newodometer(dimset,NULL,NULL); gen_chararray(dimset,vsym->data,charbuf,filler); generator->charconstant(generator,code,charbuf); bbFree(charbuf); writer(generator,vsym,code,odom->rank,odom->start,odom->count); } else /* Case 2: dim 1..n are not unlimited */ if(findunlimited(dimset,1) == rank) { size_t offset = 0; /* where are we in the data list */ size_t nelems = 0; /* # of data list items to read */ /* Create an iterator and odometer and just walk the datalist */ nc_get_iter(vsym,nciterbuffersize,&iter); odom = newodometer(dimset,NULL,NULL); for(;;offset+=nelems) { int i,uid; nelems=nc_next_iter(&iter,odom->start,odom->count); if(nelems == 0) break; bbClear(code); generator->listbegin(generator,LISTDATA,vsym->data->length,code,&uid); for(i=0;i<nelems;i++) { Constant* con = datalistith(vsym->data,i+offset); generator->list(generator,LISTDATA,uid,i,code); #ifdef USE_NOFILL if(nofill_flag && con == NULL) break; else #endif generate_basetype(basetype,con,code,filler,generator); } generator->listend(generator,LISTDATA,uid,i,code); #ifdef USE_NOFILL writer(generator,vsym,code,rank,odom->start,odom->index); #else writer(generator,vsym,code,rank,odom->start,odom->count); #endif } } else { /* Hard case: multiple unlimited dimensions */ /* Setup iterator and odometer */ nc_get_iter(vsym,nciterbuffersize,&iter); odom = newodometer(dimset,NULL,NULL); for(;;) {/* iterate in nelem chunks */ /* get nelems count and modify odometer */ size_t nelems=nc_next_iter(&iter,odom->start,odom->count); if(nelems == 0) break; generate_arrayr(vsym,code,vsym->data, odom, /*dim index=*/0, filler,generator ); #ifdef USE_NOFILL writer(generator,vsym,code,odom->rank,odom->start,odom->index); #else writer(generator,vsym,code,odom->rank,odom->start,odom->count); #endif } } odometerfree(odom); } static void generate_arrayr(Symbol* vsym, Bytebuffer* code, Datalist* list, Odometer* odom, int dimindex, Datalist* filler, Generator* generator ) { Symbol* basetype = vsym->typ.basetype; Dimset* dimset = &vsym->typ.dimset; int rank = dimset->ndims; int lastunlimited; lastunlimited = findlastunlimited(dimset); if(lastunlimited == rank) lastunlimited = 0; ASSERT(rank > 0); ASSERT(dimindex >= 0 && dimindex < rank); ASSERT(basetype->typ.typecode != NC_CHAR); if(dimindex == lastunlimited) { int uid,i; Odometer* slabodom; /* build a special odometer to walk the last few dimensions (similar to case 2 above) */ slabodom = newsubodometer(odom,dimset,dimindex,rank); /* compute the starting offset in our datalist (Assumes that slabodom->index[i] == slabodom->start[i]) */ generator->listbegin(generator,LISTDATA,list->length,code,&uid); for(i=0;odometermore(slabodom);i++) { size_t offset = odometeroffset(slabodom); Constant* con = datalistith(list,offset); #ifdef USE_NOFILL if(nofill_flag && con == NULL) break; #endif generator->list(generator,LISTDATA,uid,i,code); generate_basetype(basetype,con,code,filler,generator); odometerincr(slabodom); } generator->listend(generator,LISTDATA,uid,i,code); odometerfree(slabodom); } else { /* If we are strictly to the left of the next unlimited then our datalist is a list of compounds representing the next unlimited; so walk the subarray from this index upto next unlimited. */ int i; Odometer* slabodom; int nextunlimited = findunlimited(dimset,dimindex+1); ASSERT((dimindex < lastunlimited && (dimset->dimsyms[dimindex]->dim.isunlimited))); /* build a sub odometer */ slabodom = newsubodometer(odom,dimset,dimindex,nextunlimited); /* compute the starting offset in our datalist (Assumes that slabodom->index[i] == slabodom->start[i]) */ for(i=0;odometermore(slabodom);i++) { size_t offset = odometeroffset(slabodom); Constant* con = datalistith(list,offset); #ifdef USE_NOFILL if(nofill_flag && con == NULL) break; #endif if(!islistconst(con)) semwarn(constline(con),"Expected {...} representing unlimited list"); else { Datalist* sublist = con->value.compoundv; generate_arrayr(vsym,code,sublist,odom,nextunlimited,filler,generator); } odometerincr(slabodom); } odometerfree(slabodom); } return; } /* Generate an instance of the basetype */ void generate_basetype(Symbol* tsym, Constant* con, Bytebuffer* codebuf, Datalist* filler, Generator* generator) { Datalist* data; switch (tsym->subclass) { case NC_ENUM: case NC_OPAQUE: case NC_PRIM: if(islistconst(con)) { semerror(constline(con),"Expected primitive found {..}"); } generate_primdata(tsym,con,codebuf,filler,generator); break; case NC_COMPOUND: { int i,uid; if(con == NULL || isfillconst(con)) { Datalist* fill = (filler==NULL?getfiller(tsym):filler); ASSERT(fill->length == 1); con = &fill->data[0]; if(!islistconst(con)) semerror(con->lineno,"Compound data fill value is not enclosed in {..}"); } if(!islistconst(con)) {/* fail on no compound*/ semerror(constline(con),"Compound data must be enclosed in {..}"); } data = con->value.compoundv; generator->listbegin(generator,LISTCOMPOUND,listlength(tsym->subnodes),codebuf,&uid); for(i=0;i<listlength(tsym->subnodes);i++) { Symbol* field = (Symbol*)listget(tsym->subnodes,i); con = datalistith(data,i); generator->list(generator,LISTCOMPOUND,uid,i,codebuf); generate_basetype(field,con,codebuf,NULL,generator); } generator->listend(generator,LISTCOMPOUND,uid,i,codebuf); } break; case NC_VLEN: { Bytebuffer* vlenbuf; int uid; size_t count; if(con == NULL || isfillconst(con)) { Datalist* fill = (filler==NULL?getfiller(tsym):filler); ASSERT(fill->length == 1); con = &fill->data[0]; if(con->nctype != NC_COMPOUND) { semerror(con->lineno,"Vlen data fill value is not enclosed in {..}"); } } if(!islistconst(con)) { semerror(constline(con),"Vlen data must be enclosed in {..}"); } data = con->value.compoundv; /* generate the nc_vlen_t instance*/ vlenbuf = bbNew(); if(tsym->typ.basetype->typ.typecode == NC_CHAR) { gen_charvlen(data,vlenbuf); generator->vlenstring(generator,vlenbuf,&uid,&count); } else { generator->listbegin(generator,LISTVLEN,data->length,codebuf,&uid); for(count=0;count<data->length;count++) { generator->list(generator,LISTVLEN,uid,count,vlenbuf); generate_basetype(tsym->typ.basetype,datalistith(data,count),vlenbuf,NULL,generator); } generator->listend(generator,LISTVLEN,uid,count,codebuf,(void*)vlenbuf); } generator->vlendecl(generator,codebuf,tsym,uid,count,vlenbuf); bbFree(vlenbuf); } break; case NC_FIELD: if(tsym->typ.dimset.ndims > 0) { /* Verify that we have a sublist (or fill situation) */ if(con != NULL && !isfillconst(con) && !islistconst(con)) semerror(constline(con),"Dimensioned fields must be enclose in {...}"); generate_fieldarray(tsym->typ.basetype,con,&tsym->typ.dimset,codebuf,filler,generator); } else { generate_basetype(tsym->typ.basetype,con,codebuf,NULL,generator); } break; default: PANIC1("generate_basetype: unexpected subclass %d",tsym->subclass); } } /* Used only for structure field arrays*/ static void generate_fieldarray(Symbol* basetype, Constant* con, Dimset* dimset, Bytebuffer* codebuf, Datalist* filler, Generator* generator) { int i; int chartype = (basetype->typ.typecode == NC_CHAR); Datalist* data; ASSERT(dimset->ndims > 0); if(con != NULL && !isfillconst(con)) data = con->value.compoundv; else data = NULL; if(chartype) { /* Collect the char field in a separate buffer */ Bytebuffer* charbuf = bbNew(); gen_chararray(dimset,data,charbuf,filler); generator->charconstant(generator,codebuf,charbuf); bbFree(charbuf); } else { int uid; size_t xproduct = crossproduct(dimset,0,0); /* compute total number of elements */ generator->listbegin(generator,LISTFIELDARRAY,xproduct,codebuf,&uid); for(i=0;i<xproduct;i++) { con = (data == NULL ? NULL : datalistith(data,i)); generator->list(generator,LISTFIELDARRAY,uid,i,codebuf); generate_basetype(basetype,con,codebuf,NULL,generator); } generator->listend(generator,LISTFIELDARRAY,uid,i,codebuf); } } static void generate_primdata(Symbol* basetype, Constant* prim, Bytebuffer* codebuf, Datalist* filler, Generator* generator) { Constant target; if(prim == NULL || isfillconst(prim)) { Datalist* fill = (filler==NULL?getfiller(basetype):filler); ASSERT(fill->length == 1); prim = datalistith(fill,0); } ASSERT(prim->nctype != NC_COMPOUND); target.nctype = basetype->typ.typecode; if(target.nctype != NC_ECONST) { convert1(prim,&target); } switch (target.nctype) { case NC_ECONST: if(basetype->subclass != NC_ENUM) { semerror(constline(prim),"Conversion to enum not supported (yet)"); } break; case NC_OPAQUE: setprimlength(&target,basetype->typ.size*2); break; default: break; } generator->constant(generator,&target,codebuf); return; }
netcdfgroup
archives: