1/*********************************************************************
2 *   Copyright 2009, UCAR/Unidata
3 *   See netcdf/COPYRIGHT file for copying and redistribution conditions.
4 *********************************************************************/
5
6#include "includes.h"
7#include "nc_iter.h"
8#include "odom.h"
9#include "offsets.h"
10
11/**************************************************/
12/* Code for generating data lists*/
13/**************************************************/
14/* For datalist constant rules: see the rules on the man page */
15
16/* Forward*/
17static void generate_array(Symbol*,Bytebuffer*,Datalist*,Generator*,Writer);
18static void generate_arrayr(Symbol*,Bytebuffer*,Datalist*,Odometer*,int,Datalist*,Generator*);
19static void generate_primdata(Symbol*, NCConstant*, Bytebuffer*, DatalistfillsrcGenerator*);
20static void generate_fieldarray(Symbol*, NCConstant*, Dimset*, Bytebuffer*, DatalistfillsrcGenerator*);
21
22/* Mnemonics */
23#define VLENLIST1
24#define FIELDARRAY 1
25
26/**************************************************/
27
28
29int
30generator_getstate(Generatorgenerator ,void** statep)
31{
32    if(statep) *statep = (void*)generator->globalstate;
33    return 1;
34}
35
36int generator_reset(Generatorgenerator, void* state)
37{
38    generator->globalstate = state;
39    return 1;
40}
41
42#ifdef IGNORe
43static void
44checkodom(Odometerodom)
45{
46    int i;
47    for(i=0;i<odom->rank;i++) {
48 ASSERT(odom->index[i] == odom->start[i]+odom->count[i]);
49    }
50}
51#endif
52
53/**************************************************/
54
55void
56generate_attrdata(SymbolasymGeneratorgeneratorWriter writerBytebuffercodebuf)
57{
58    Symbolbasetype = asym->typ.basetype;
59    nc_type typecode = basetype->typ.typecode;
60
61   if(typecode == NC_CHAR) {
62 gen_charattr(asym->data,codebuf);
63    } else {
64 int uid;
65 size_t count;
66        generator->listbegin(generator,asym,NULL,LISTATTR,asym->data->length,codebuf,&uid);
67        for(count=0;count<asym->data->length;count++) {
68            NCConstantcon = datalistith(asym->data,count);
69            generator->list(generator,asym,NULL,LISTATTR,uid,count,codebuf);
70            generate_basetype(asym->typ.basetype,con,codebuf,NULL,generator);
71 }
72        generator->listend(generator,asym,NULL,LISTATTR,uid,count,codebuf);
73    }
74    writer(generator,asym,codebuf,0,NULL,NULL);
75}
76
77void
78generate_vardata(SymbolvsymGeneratorgeneratorWriter writerBytebuffercode)
79{
80    Dimsetdimset = &vsym->typ.dimset;
81    int rank = dimset->ndims;
82    Symbolbasetype = vsym->typ.basetype;
83    Datalistfiller = getfiller(vsym);
84#if 0
85    const size_t* start;
86    const size_t* count;
87    Odometerodom;
88#endif
89
90    if(vsym->data == NULL) return;
91
92    /* give the buffer a running start to be large enough*/
93    if(!bbSetalloc(codenciterbuffersize))
94      return;
95
96    if(rank == 0) {/*scalar case*/
97        NCConstantc0 = datalistith(vsym->data,0);
98        generate_basetype(basetype,c0,code,filler,generator);
99        writer(generator,vsym,code,0,NULL,NULL);
100    } else {/*rank > 0*/
101#if 0
102        /* First, create an odometer using all of the dimensions */
103        odom = newodometer(dimset,NULL,NULL);
104 start = odometerstartvector(odom);
105 count = odometercountvector(odom);
106#endif
107 generate_array(vsym,code,filler,generator,writer);
108    }
109}
110
111
112/**
113
114The basic idea is to split the set of dimensions into
115groups and iterate over each group by recursion.
116
117A group is defined as the range of indices starting at an
118unlimited dimension upto (but not including) the next
119unlimited.
120
121The first group starts at index 0, even if dimension 0 is not
122unlimited.  The last group is everything from the last
123unlimited dimension thru the last dimension (index rank-1).
124
125*/
126
127static void
128generate_array(Symbolvsym,
129               Bytebuffercode,
130               Datalistfiller,
131               Generatorgenerator,
132        Writer writer
133              )
134{
135    Dimsetdimset = &vsym->typ.dimset;
136    int rank = dimset->ndims;
137    Symbolbasetype = vsym->typ.basetype;
138    nc_type typecode = basetype->typ.typecode;
139    Odometerodom = NULL;
140    nciter_t iter;
141    int firstunlim = findunlimited(dimset,1);
142    int nunlim = countunlimited(dimset);
143    int isnc3unlim = (nunlim <= 1 && (firstunlim == 0 || firstunlim == rank)); /* netcdf-3 case of at most 1 unlim in 0th dimension */
144
145    ASSERT(rank > 0);
146
147    if(isnc3unlim) {
148        /* Handle NC_CHAR case separately */
149        if(typecode == NC_CHAR) {
150            Bytebuffercharbuf = bbNew();
151            gen_chararray(dimset,0,vsym->data,charbuf,filler);
152     generator->charconstant(generator,vsym,code,charbuf);
153     /* Create an odometer to get the dimension info */
154            odom = newodometer(dimset,NULL,NULL);
155            writer(generator,vsym,code,odom->rank,odom->start,odom->count);
156#if 0
157            writer(generator,vsym,code,odom->rank,0,bbLength(charbuf));
158#endif
159     bbFree(charbuf);
160 } else { /* typecode != NC_CHAR */
161            /* Case: dim 1..rank-1 are not unlimited, dim 0 might be */
162            size_t offset = 0; /* where are we in the data list */
163            size_t nelems = 0; /* # of data list items to generate */
164            /* Create an iterator and odometer and just walk the datalist */
165            nc_get_iter(vsym,nciterbuffersize,&iter);
166            odom = newodometer(dimset,NULL,NULL);
167            for(;;offset+=nelems) {
168                int i,uid;
169                nelems=nc_next_iter(&iter,odometerstartvector(odom),odometercountvector(odom));
170                if(nelems == 0)
171     break;
172                bbClear(code);
173                generator->listbegin(generator,vsym,NULL,LISTDATA,vsym->data->length,code,&uid);
174                for(i=0;i<nelems;i++) {
175                    NCConstantcon = datalistith(vsym->data,i+offset);
176                    generator->list(generator,vsym,NULL,LISTDATA,uid,i,code);
177                    generate_basetype(basetype,con,code,filler,generator);
178                }
179                generator->listend(generator,vsym,NULL,LISTDATA,uid,i,code);
180                writer(generator,vsym,code,rank,odom->start,odom->count);
181            }
182 }
183    } else { /* Hard case: multiple unlimited dimensions or unlim in dim > 0*/
184        /* Setup iterator and odometer */
185        nc_get_iter(vsym,NC_MAX_UINT,&iter); /* effectively infinite */
186        odom = newodometer(dimset,NULL,NULL);
187        for(;;) {/* iterate in nelem chunks */
188            /* get nelems count and modify odometer */
189            size_t nelems=nc_next_iter(&iter,odom->start,odom->count);
190            if(nelems == 0) break;
191            generate_arrayr(vsym,code,vsym->data,
192                            odom,
193                            /*dim index=*/0,
194                            filler,generator
195                           );
196            writer(generator,vsym,code,odom->rank,odom->start,odom->count);
197        }
198    }
199    if(odom != NULL)
200        odometerfree(odom);
201}
202
203/**
204The basic idea is to split the set of dimensions into groups
205and iterate over each group.  A group is defined as the
206range of indices starting at an unlimited dimension upto
207(but not including) the next unlimited.  The first group
208starts at index 0, even if dimension 0 is not unlimited.
209The last group is everything from the last unlimited
210dimension thru the last dimension (index rank-1).
211*/
212static void
213generate_arrayr(Symbolvsym,
214               Bytebuffercode,
215               Datalistlist,
216               Odometerodom,
217               int dimindex,
218               Datalistfiller,
219               Generatorgenerator
220              )
221{
222    int uid,i;
223    Symbolbasetype = vsym->typ.basetype;
224    Dimsetdimset = &vsym->typ.dimset;
225    int rank = dimset->ndims;
226    int lastunlimited = findlastunlimited(dimset);
227    int nextunlimited = findunlimited(dimset,dimindex+1);
228    int typecode = basetype->typ.typecode;
229    int islastgroup = (lastunlimited == rank || dimindex >= lastunlimited || dimindex == rank-1);
230    Odometersubodom = NULL;
231
232    ASSERT(rank > 0);
233    ASSERT((dimindex >= 0 && dimindex < rank));
234
235    if(islastgroup) {
236        /* Handle NC_CHAR case separately */
237        if(typecode == NC_CHAR) {
238            Bytebuffercharbuf = bbNew();
239            gen_chararray(dimset,dimindex,list,charbuf,filler);
240     generator->charconstant(generator,vsym,code,charbuf);
241     bbFree(charbuf);
242 } else {
243            /* build a special odometer to walk the last few dimensions */
244            subodom = newsubodometer(odom,dimset,dimindex,rank);
245            generator->listbegin(generator,vsym,NULL,LISTDATA,list->length,code,&uid);
246            for(i=0;odometermore(subodom);i++) {
247                size_t offset = odometeroffset(subodom);
248                NCConstantcon = datalistith(list,offset);
249                generator->list(generator,vsym,NULL,LISTDATA,uid,i,code);
250                generate_basetype(basetype,con,code,filler,generator);
251                odometerincr(subodom);
252            }
253            generator->listend(generator,vsym,NULL,LISTDATA,uid,i,code);
254            odometerfree(subodom); subodom = NULL;
255 }
256    } else {/* !islastgroup */
257        /* Our datalist must be a list of compounds representing
258           the next unlimited; so walk the subarray from this index
259           upto next unlimited.
260        */
261        ASSERT((dimindex < nextunlimited));
262        ASSERT((isunlimited(dimset,nextunlimited)));
263        /* build a sub odometer */
264        subodom = newsubodometer(odom,dimset,dimindex,nextunlimited);
265        for(i=0;odometermore(subodom);i++) {
266            size_t offset = odometeroffset(subodom);
267            NCConstantcon = datalistith(list,offset);
268            if(con == NULL || con->nctype == NC_FILL) {
269                if(filler == NULL)
270                    filler = getfiller(vsym);
271                generate_arrayr(vsym,code,filler,odom,nextunlimited,NULL,generator);
272
273            } else if(islistconst(con)) {
274                Datalistsublist = compoundfor(con);
275                generate_arrayr(vsym,code,sublist,odom,nextunlimited,filler,generator);
276            } else {
277                semerror(constline(con),"Expected {...} representing unlimited list");
278                return;
279            }
280            odometerincr(subodom);
281        }
282        odometerfree(subodom); subodom = NULL;
283    }
284    if(subodom != NULL)
285        odometerfree(subodom);
286    return;
287}
288
289/* Generate an instance of the basetype */
290void
291generate_basetype(SymboltsymNCConstantconBytebuffercodebufDatalistfillerGeneratorgenerator)
292{
293    Datalistdata;
294    int offsetbase = 0;
295
296    switch (tsym->subclass) {
297
298    case NC_ENUM:
299    case NC_OPAQUE:
300    case NC_PRIM:
301        if(islistconst(con)) {
302            semerror(constline(con),"Expected primitive found {..}");
303        }
304        generate_primdata(tsym,con,codebuf,filler,generator);
305        break;
306
307    case NC_COMPOUND: {
308        int i,uidnfieldsdllen;
309        if(con == NULL || isfillconst(con)) {
310            Datalistfill = (filler==NULL?getfiller(tsym):filler);
311            ASSERT(fill->length == 1);
312            con = &fill->data[0];
313            if(!islistconst(con)) {
314              if(con)
315                semerror(con->lineno,"Compound data fill value is not enclosed in {..}");
316              else
317                semerror(0,"Compound data fill value not enclosed in {..}, con is NULL.");
318            }
319        }
320
321        if(!con) { /* fail on null compound. */
322          semerror(constline(con),"NULL compound data.");
323          break;
324        }
325
326        if(!islistconst(con)) {/* fail on no compound*/
327            semerror(constline(con),"Compound data must be enclosed in {..}");
328        }
329
330        data = con->value.compoundv;
331        nfields = listlength(tsym->subnodes);
332        dllen = datalistlen(data);
333        if(dllen > nfields) {
334          semerror(con->lineno,"Datalist longer than the number of compound fields");
335            break;
336        }
337        generator->listbegin(generator,tsym,&offsetbase,LISTCOMPOUND,listlength(tsym->subnodes),codebuf,&uid);
338        for(i=0;i<nfields;i++) {
339            Symbolfield = (Symbol*)listget(tsym->subnodes,i);
340            con = datalistith(data,i);
341            generator->list(generator,field,&offsetbase,LISTCOMPOUND,uid,i,codebuf);
342            generate_basetype(field,con,codebuf,NULL,generator);
343        }
344        generator->listend(generator,tsym,&offsetbase,LISTCOMPOUND,uid,i,codebuf);
345        } break;
346
347    case NC_VLEN: {
348        Bytebuffervlenbuf;
349        int uid;
350        size_t count;
351
352        if(con == NULL || isfillconst(con)) {
353            Datalistfill = (filler==NULL?getfiller(tsym):filler);
354            ASSERT(fill->length == 1);
355            con = &fill->data[0];
356            if(con->nctype != NC_COMPOUND) {
357                semerror(con->lineno,"Vlen data fill value is not enclosed in {..}");
358            }
359        }
360
361        if(!islistconst(con)) {
362            semerror(constline(con),"Vlen data must be enclosed in {..}");
363        }
364        data = con->value.compoundv;
365        /* generate the nc_vlen_t instance*/
366        vlenbuf = bbNew();
367        if(tsym->typ.basetype->typ.typecode == NC_CHAR) {
368            gen_charvlen(data,vlenbuf);
369            generator->vlenstring(generator,tsym,vlenbuf,&uid,&count);
370        } else {
371            generator->listbegin(generator,tsym,NULL,LISTVLEN,data->length,codebuf,&uid);
372            for(count=0;count<data->length;count++) {
373              NCConstantcon;
374                generator->list(generator,tsym,NULL,LISTVLEN,uid,count,vlenbuf);
375                con = datalistith(data,count);
376                generate_basetype(tsym->typ.basetype,con,vlenbuf,NULL,generator);
377            }
378            generator->listend(generator,tsym,NULL,LISTVLEN,uid,count,codebuf,(void*)vlenbuf);
379        }
380        generator->vlendecl(generator,tsym,codebuf,uid,count,vlenbuf);
381        bbFree(vlenbuf);
382        } break;
383
384    case NC_FIELD:
385        if(tsym->typ.dimset.ndims > 0) {
386            /* Verify that we have a sublist (or fill situation) */
387            if(con != NULL && !isfillconst(con) && !islistconst(con))
388                semerror(constline(con),"Dimensioned fields must be enclose in {...}");
389            generate_fieldarray(tsym->typ.basetype,con,&tsym->typ.dimset,codebuf,filler,generator);
390        } else {
391            generate_basetype(tsym->typ.basetype,con,codebuf,NULL,generator);
392        }
393        break;
394
395    default: PANIC1("generate_basetype: unexpected subclass %d",tsym->subclass);
396    }
397}
398
399/* Used only for structure field arrays*/
400static void
401generate_fieldarray(SymbolbasetypeNCConstantconDimsetdimset,
402                 BytebuffercodebufDatalistfillerGeneratorgenerator)
403{
404    int i;
405    int chartype = (basetype->typ.typecode == NC_CHAR);
406    Datalistdata;
407    int rank = rankfor(dimset);
408
409    ASSERT(dimset->ndims > 0);
410
411    if(con != NULL && !isfillconst(con))
412        data = con->value.compoundv;
413    else
414        data = NULL;
415
416    if(chartype) {
417        Bytebuffercharbuf = bbNew();
418        gen_chararray(dimset,0,data,charbuf,filler);
419        generator->charconstant(generator,basetype,codebuf,charbuf);
420        bbFree(charbuf);
421    } else {
422        int uid;
423        size_t xproduct = crossproduct(dimset,0,rank); /* compute total number of elements */
424        generator->listbegin(generator,basetype,NULL,LISTFIELDARRAY,xproduct,codebuf,&uid);
425        for(i=0;i<xproduct;i++) {
426            con = (data == NULL ? NULL : datalistith(data,i));
427            generator->list(generator,basetype,NULL,LISTFIELDARRAY,uid,i,codebuf);
428            generate_basetype(basetype,con,codebuf,NULL,generator);
429        }
430        generator->listend(generator,basetype,NULL,LISTFIELDARRAY,uid,i,codebuf);
431    }
432}
433
434
435/* An opaque string value might not conform
436   to the size of the opaque to which it is being
437   assigned. Normalize it to match the required
438   opaque length (in bytes).
439   Note that the string is a sequence of nibbles (4 bits).
440*/
441static void
442normalizeopaquelength(NCConstantprim, unsigned long nbytes)
443{
444    int nnibs = 2*nbytes;
445    ASSERT(prim->nctype==NC_OPAQUE);
446    if(prim->value.opaquev.len == nnibs) {
447        /* do nothing*/
448    } else if(prim->value.opaquev.len > nnibs) { /* truncate*/
449        prim->value.opaquev.stringv[nnibs] = '\0';
450        prim->value.opaquev.len = nnibs;
451    } else {/* prim->value.opaquev.len < nnibs => expand*/
452        char* s;
453        s = (char*)emalloc(nnibs+1);
454        memset(s,'0',nnibs);    /* Fill with '0' characters */
455        memcpy(s,prim->value.opaquev.stringv,prim->value.opaquev.len);
456        s[nnibs] = '\0';
457        efree(prim->value.opaquev.stringv);
458        prim->value.opaquev.stringv=s;
459        prim->value.opaquev.len = nnibs;
460    }
461}
462
463static void
464generate_primdata(SymbolbasetypeNCConstantprimBytebuffercodebuf,
465                  DatalistfillerGeneratorgenerator)
466{
467    NCConstant target;
468    int match;
469
470    if(prim == NULL || isfillconst(prim)) {
471        Datalistfill = (filler==NULL?getfiller(basetype):filler);
472        ASSERT(fill->length == 1);
473        prim = datalistith(fill,0);
474    }
475
476    ASSERT((prim->nctype != NC_COMPOUND));
477
478    /* Verify that the constant is consistent with the type */
479    match = 1;
480    switch (prim->nctype) {
481    case NC_CHAR:
482    case NC_BYTE:
483    case NC_SHORT:
484    case NC_INT:
485    case NC_FLOAT:
486    case NC_DOUBLE:
487    case NC_UBYTE:
488    case NC_USHORT:
489    case NC_UINT:
490    case NC_INT64:
491    case NC_UINT64:
492    case NC_STRING:
493        match = (basetype->subclass == NC_PRIM ? 1 : 0);
494        break;
495
496#ifdef USE_NETCDF4
497    case NC_NIL:
498        match = (basetype->subclass == NC_PRIM && basetype->typ.typecode == NC_STRING ? 1 : 0);
499        break;
500
501    case NC_OPAQUE:
502        /* OPAQUE is also consistent with numbers */
503        match = (basetype->subclass == NC_OPAQUE
504                 || basetype->subclass == NC_PRIM ? 1 : 0);
505        break;
506    case NC_ECONST:
507        match = (basetype->subclass == NC_ENUM ? 1 : 0);
508        if(match) {
509            /* Make sure this econst belongs to this enum */
510            Symbolec = prim->value.enumv;
511            Symbolen = ec->container;
512            match = (en == basetype);
513        }
514        break;
515#endif
516    default:
517        match = 0;
518    }
519    if(!match) {
520        semerror(constline(prim),"Data value is not consistent with the expected type: %s",
521                 basetype->name);
522    }
523
524    target.nctype = basetype->typ.typecode;
525
526    if(target.nctype != NC_ECONST) {
527        convert1(prim,&target);
528    }
529
530    switch (target.nctype) {
531    case NC_ECONST:
532        if(basetype->subclass != NC_ENUM) {
533            semerror(constline(prim),"Conversion to enum not supported (yet)");
534        } break;
535     case NC_OPAQUE:
536        normalizeopaquelength(&target,basetype->typ.size);
537        break;
538    default:
539        break;
540    }
541    generator->constant(generator,basetype,&target,codebuf);
542
543    return;
544}


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed