1/*********************************************************************
2 *   Copyright 1993, UCAR/Unidata
3 *   See netcdf/COPYRIGHT file for copying and redistribuution conditions.
4 *********************************************************************/
5
6
7#include "ncdap.h"
8#include "dapodom.h"
9#include "dapdump.h"
10#include "ncd2dispatch.h"
11#include "ocx.h"
12
13#define NEWVARM
14
15static DCEnodesave = NULL;
16
17/* Define a tracker for memory to support*/
18/* the concatenation*/
19struct NCMEMORY {
20    void* memory;
21    char* next; /* where to store the next chunk of data*/
22};
23
24/* Forward:*/
25static NCerror moveto(NCDAPCOMMON*, Getvara*, CDFnodedataroot, void* memory);
26static NCerror movetor(NCDAPCOMMON*, OCdatanode currentcontent,
27    NClistpath, int depth,
28    Getvara*, size_t dimindex,
29    struct NCMEMORY*, NClistsegments);
30static NCerror movetofield(NCDAPCOMMON*, OCdatanode,
31    NClist*, int depth,
32        Getvara*, size_t dimindex,
33        struct NCMEMORY*, NClistsegments);
34
35static int findfield(CDFnodenodeCDFnodesubnode);
36static NCerror removepseudodims(DCEprojectionproj);
37
38static int extract(NCDAPCOMMON*, Getvara*, CDFnode*, DCEsegment*, size_t dimindexOClinkOCdatanode, struct NCMEMORY*);
39static int extractstring(NCDAPCOMMON*, Getvara*, CDFnode*, DCEsegment*, size_t dimindexOClinkOCdatanode, struct NCMEMORY*);
40static void freegetvara(Getvaravara);
41static NCerror makegetvar(NCDAPCOMMON*, CDFnode*, void*, nc_typeGetvara**);
42static NCerror attachsubset(CDFnodetargetCDFnodepattern);
43
44/**************************************************/
45/**
461. We build the projection to be sent to the server aka
47the fetch constraint.  We want the server do do as much work
48as possible, so we send it a url with a fetch constraint
49that is the merge of the url constraint with the vara
50constraint.
51
52The url constraint, if any, is the one that was provided
53in the url specified in nc_open().
54
55The vara constraint is the one formed from the arguments
56(start, count, stride) provided to the call to nc_get_vara().
57
58There are some exceptions to the formation of the fetch constraint.
59In all cases, the fetch constraint will use any URL selections,
60but will use different fetch projections.
61a. URL is unconstrainable (e.g. file://...):
62    fetchprojection = null => fetch whole dataset
63b. The target variable (as specified in nc_get_vara())
64   is already in the cache and is whole variable, but note
65   that it might be part of the prefetch mixed in with other prefetched
66   variables.
67    fetchprojection = N.A. since variable is in the cache
68c. Vara is requesting part of a variable but NCF_WHOLEVAR flag is set.
69    fetchprojection = unsliced vara variable => fetch whole variable
70d. Vara is requesting part of a variable and NCF_WHOLEVAR flag is not set.
71    fetchprojection = sliced vara variable => fetch part variable
72
732. At this point, all or part of the target variable is available in the cache.
74
753. We build a projection to walk (guide) the use of the oc
76   data procedures to extract the required data from the cache.
77   For cases a,b,c:
78       walkprojection = merge(urlprojection,varaprojection)
79   For case d:
80       walkprojection =  varaprojection without slicing.
81       This means we need only extract the complete contents of the cache.
82       Notice that this will not necessarily be a direct memory to
83       memory copy because the dap encoding still needs to be
84       interpreted. For this case, we derive a walk projection
85       from the vara projection that will properly access the cached data.
86       This walk projection shifts the merged projection so all slices
87       start at 0 and have a stride of 1.
88*/
89
90NCerror
91nc3d_getvarx(int ncid, int varid,
92     const size_t *startp,
93     const size_t *countp,
94     const ptrdiff_tstridep,
95     void *data,
96     nc_type dsttype0)
97{
98    NCerror ncstat = NC_NOERR;
99    OCerror ocstat = OC_NOERR;
100    int i;
101    NCdrno;
102    NCsubstrate;
103    NCDAPCOMMONdapcomm;
104    CDFnodecdfvar = NULL; /* cdf node mapping to var*/
105    NClistvarnodes;
106    nc_type dsttype;
107    Getvaravarainfo = NULL;
108    CDFnodextarget = NULL; /* target in DATADDS */
109    CDFnodetarget = NULL; /* target in constrained DDS */
110    DCEprojectionvaraprojection = NULL;
111    NCcachenodecachenode = NULL;
112    size_t localcount[NC_MAX_VAR_DIMS];
113    NClistncdimsall;
114    size_t ncrank;
115    NClistvars = NULL;
116    DCEconstraintfetchconstraint = NULL;
117    DCEprojectionfetchprojection = NULL;
118    DCEprojectionwalkprojection = NULL;
119    int state;
120#define FETCHWHOLE 1 /* fetch whole data set */
121#define FETCHVAR   2 /* fetch whole variable */
122#define FETCHPART  4 /* fetch constrained variable */
123#define CACHED     8 /* whole variable is already in the cache */
124
125    ncstat = NC_check_id(ncid, (NC**)&drno);
126    if(ncstat != NC_NOERR) goto fail;
127    dapcomm = (NCDAPCOMMON*)drno->dispatchdata;
128
129    ncstat = NC_check_id(getnc3id(drno), (NC**)&substrate);
130    if(ncstat != NC_NOERR) goto fail;
131
132    /* Locate var node via varid */
133    varnodes = dapcomm->cdf.ddsroot->tree->varnodes;
134    for(i=0;i<nclistlength(varnodes);i++) {
135 CDFnodenode = (CDFnode*)nclistget(varnodes,i);
136 if(node->array.basevar == NULL
137           && node->nctype == NC_Atomic
138           && node->ncid == varid) {
139     cdfvar = node;
140     break;
141 }
142    }
143
144    ASSERT((cdfvar != NULL));
145
146    /* If the variable is prefetchable, then now
147       is the time to do a lazy prefetch */
148   if(FLAGSET(dapcomm->controls,NCF_PREFETCH)
149      && !FLAGSET(dapcomm->controls,NCF_PREFETCH_EAGER)) {
150        if(dapcomm->cdf.cache != NULL && dapcomm->cdf.cache->prefetch == NULL) {
151            ncstat = prefetchdata(dapcomm);
152            if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;}
153 }
154    }
155
156    /* Get the dimension info */
157    ncdimsall = cdfvar->array.dimsetall;
158    ncrank = nclistlength(ncdimsall);
159
160#ifdef DEBUG
161 {
162int i;
163fprintf(stderr,"getvarx: %s",cdfvar->ncfullname);
164for(i=0;i<ncrank;i++)
165  fprintf(stderr,"(%ld:%ld:%ld)",
166 (long)startp[i],
167 (long)countp[i],
168 (long)stridep[i]
169 );
170fprintf(stderr,"\n");
171 }
172#endif
173
174    /* Fill in missing arguments */
175    if(startp == NULL)
176 startp = nc_sizevector0;
177
178    if(countp == NULL) {
179        /* Accumulate the dimension sizes */
180        for(i=0;i<ncrank;i++) {
181     CDFnodedim = (CDFnode*)nclistget(ncdimsall,i);
182     localcount[i] = dim->dim.declsize;
183 }
184 countp = localcount;
185    }
186
187    if(stridep == NULL)
188 stridep = nc_ptrdiffvector1;
189
190    /* Validate the dimension sizes */
191    for(i=0;i<ncrank;i++) {
192      CDFnodedim = (CDFnode*)nclistget(ncdimsall,i);
193      /* countp and startp are unsigned, so will never be < 0 */
194      if(stridep[i] < 1) {
195 ncstat = NC_EINVALCOORDS;
196 goto fail;
197      }
198      if(startp[i] >= dim->dim.declsize
199  || startp[i]+(stridep[i]*(countp[i]-1)) >= dim->dim.declsize) {
200 ncstat = NC_EINVALCOORDS;
201 goto fail;
202      }
203    }
204
205#ifdef DEBUG
206 {
207NClistdims = cdfvar->array.dimsetall;
208fprintf(stderr,"getvarx: %s",cdfvar->ncfullname);
209if(nclistlength(dims) > 0) {int i;
210for(i=0;i<nclistlength(dims);i++)
211fprintf(stderr,"(%lu:%lu:%lu)",(unsigned long)startp[i],(unsigned long)countp[i],(unsigned long)stridep[i]);
212fprintf(stderr," -> ");
213for(i=0;i<nclistlength(dims);i++)
214if(stridep[i]==1)
215fprintf(stderr,"[%lu:%lu]",(unsigned long)startp[i],(unsigned long)((startp[i]+countp[i])-1));
216else {
217unsigned long iend = (stridep[i] * countp[i]);
218iend = (iend + startp[i]);
219iend = (iend - 1);
220fprintf(stderr,"[%lu:%lu:%lu]",
221(unsigned long)startp[i],(unsigned long)stridep[i],iend);
222 }
223 }
224fprintf(stderr,"\n");
225 }
226#endif
227
228    dsttype = (dsttype0);
229
230    /* Default to using the inquiry type for this var*/
231    if(dsttype == NC_NATdsttype = cdfvar->externaltype;
232
233    /* Validate any implied type conversion*/
234    if(cdfvar->etype != dsttype && dsttype == NC_CHAR) {
235        /* The only disallowed conversion is to/from char and non-byte
236           numeric types*/
237 switch (cdfvar->etype) {
238 case NC_STRING: case NC_URL:
239 case NC_CHAR: case NC_BYTE: case NC_UBYTE:
240     break;
241 default:
242     return THROW(NC_ECHAR);
243 }
244    }
245
246    ncstat = makegetvar(dapcomm,cdfvar,data,dsttype,&varainfo);
247    if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;}
248
249    /* Compile the start/stop/stride info into a projection */
250    ncstat = dapbuildvaraprojection(varainfo->target,
251                   startp,countp,stridep,
252                                  &varaprojection);
253    if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;}
254
255    fetchprojection = NULL;
256    walkprojection = NULL;
257
258    /* Create walkprojection as the merge of the url projections
259       and the vara projection; may change in FETCHPART case below*/
260    ncstat = daprestrictprojection(dapcomm->oc.dapconstraint->projections,
261    varaprojection,&walkprojection);
262    if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;}
263
264#ifdef DEBUG
265fprintf(stderr,"getvarx: walkprojection: |%s|\n",dumpprojection(walkprojection));
266#endif
267
268    /* define the var list of interest */
269    vars = nclistnew();
270    nclistpush(vars,(void*)varainfo->target);
271
272    state = 0;
273    if(iscached(dapcomm,cdfvar,&cachenode)) { /* ignores non-whole variable cache entries */
274 state = CACHED;
275 ASSERT((cachenode != NULL));
276#ifdef DEBUG
277fprintf(stderr,"var is in cache\n");
278#endif
279        /* If it is cached, then it is a whole variable but may still
280           need to apply constraints during the walk */
281 ASSERT(cachenode->wholevariable); /* by construction */
282    } else if(FLAGSET(dapcomm->controls,NCF_UNCONSTRAINABLE)) {
283 state = FETCHWHOLE;
284    } else {/* load using constraints */
285        if(FLAGSET(dapcomm->controls,NCF_WHOLEVAR))
286     state = FETCHVAR;
287 else
288     state = FETCHPART;
289    }
290    ASSERT(state != 0);
291
292    switch (state) {
293
294    case FETCHWHOLE: {
295        /* buildcachenode3 will create a new cachenode and
296           will also fetch the whole corresponding datadds.
297 */
298        /* Build the complete constraint to use in the fetch */
299        fetchconstraint = (DCEconstraint*)dcecreate(CES_CONSTRAINT);
300        /* Use no projections or selections */
301        fetchconstraint->projections = nclistnew();
302        fetchconstraint->selections = nclistnew();
303#ifdef DEBUG
304fprintf(stderr,"getvarx: FETCHWHOLE: fetchconstraint: %s\n",dumpconstraint(fetchconstraint));
305#endif
306        ncstat = buildcachenode(dapcomm,fetchconstraint,vars,&cachenode,0);
307 fetchconstraint = NULL; /*buildcachenode34 takes control of fetchconstraint.*/
308 if(ncstat != NC_NOERR) {THROWCHK(ncstat); nullfree(varainfo);
309                                                varainfo=NULL;
310                                                goto fail;}
311    } break;
312
313    case CACHED: {
314    } break;
315
316    case FETCHVAR: { /* Fetch a complete single variable */
317        /* Create fetch projection as the merge of the url projections
318           and the vara projection */
319        ncstat = daprestrictprojection(dapcomm->oc.dapconstraint->projections,
320        varaprojection,&fetchprojection);
321 /* elide any sequence and string dimensions (dap servers do not allow such). */
322 ncstat = removepseudodims(fetchprojection);
323        if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;}
324
325 /* Convert to a whole variable projection */
326 dcemakewholeprojection(fetchprojection);
327
328#ifdef DEBUG
329    fprintf(stderr,"getvarx: FETCHVAR: fetchprojection: |%s|\n",dumpprojection(fetchprojection));
330#endif
331
332        /* Build the complete constraint to use in the fetch */
333        fetchconstraint = (DCEconstraint*)dcecreate(CES_CONSTRAINT);
334        /* merged constraint just uses the url constraint selection */
335        fetchconstraint->selections = dceclonelist(dapcomm->oc.dapconstraint->selections);
336 /* and the created fetch projection */
337        fetchconstraint->projections = nclistnew();
338        nclistpush(fetchconstraint->projections,(void*)fetchprojection);
339#ifdef DEBUG
340fprintf(stderr,"getvarx: FETCHVAR: fetchconstraint: %s\n",dumpconstraint(fetchconstraint));
341#endif
342        /* buildcachenode3 will create a new cachenode and
343           will also fetch the corresponding datadds.
344        */
345 ncstat = buildcachenode(dapcomm,fetchconstraint,vars,&cachenode,0);
346        fetchconstraint = NULL; /*buildcachenode34 takes control of fetchconstraint.*/
347 if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;}
348    } break;
349
350    case FETCHPART: {
351        /* Create fetch projection as the merge of the url projections
352           and the vara projection */
353        ncstat = daprestrictprojection(dapcomm->oc.dapconstraint->projections,
354        varaprojection,&fetchprojection);
355 /* elide any sequence and string dimensions (dap servers do not allow such). */
356 ncstat = removepseudodims(fetchprojection);
357        if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;}
358
359 /* Shift the varaprojection for simple walk */
360 dcefree((DCEnode*)walkprojection) ; /* reclaim any existing walkprojection */
361 walkprojection = (DCEprojection*)dceclone((DCEnode*)varaprojection);
362        dapshiftprojection(walkprojection);
363
364#ifdef DEBUG
365        fprintf(stderr,"getvarx: FETCHPART: fetchprojection: |%s|\n",dumpprojection(fetchprojection));
366#endif
367
368        /* Build the complete constraint to use in the fetch */
369        fetchconstraint = (DCEconstraint*)dcecreate(CES_CONSTRAINT);
370        /* merged constraint just uses the url constraint selection */
371        fetchconstraint->selections = dceclonelist(dapcomm->oc.dapconstraint->selections);
372 /* and the created fetch projection */
373        fetchconstraint->projections = nclistnew();
374        nclistpush(fetchconstraint->projections,(void*)fetchprojection);
375#ifdef DEBUG
376        fprintf(stderr,"getvarx: FETCHPART: fetchconstraint: %s\n",dumpconstraint(fetchconstraint));
377#endif
378        /* buildcachenode3 will create a new cachenode and
379           will also fetch the corresponding datadds.
380        */
381        ncstat = buildcachenode(dapcomm,fetchconstraint,vars,&cachenode,0);
382
383 fetchconstraint = NULL; /*buildcachenode34 takes control of fetchconstraint.*/
384 if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;}
385    } break;
386
387    default: PANIC1("unknown fetch state: %d\n",state);
388    }
389
390    ASSERT(cachenode != NULL);
391
392#ifdef DEBUG
393fprintf(stderr,"cache.datadds=%s\n",dumptree(cachenode->datadds));
394#endif
395
396    /* attach DATADDS to (constrained) DDS */
397    unattach(dapcomm->cdf.ddsroot);
398    ncstat = attachsubset(cachenode->datadds,dapcomm->cdf.ddsroot);
399    if(ncstat) goto fail;
400
401    /* Fix up varainfo to use the cache */
402    varainfo->cache = cachenode;
403    cachenode = NULL;
404    varainfo->varaprojection = walkprojection;
405    walkprojection = NULL;
406
407    /* Get the var correlate from the datadds */
408    target = varainfo->target;
409    xtarget = target->attachment;
410    if(xtarget == NULL)
411 {THROWCHK(ncstat=NC_ENODATA); goto fail;}
412
413    /* Switch to datadds tree space*/
414    varainfo->target = xtarget;
415save = (DCEnode*)varaprojection;
416    ncstat = moveto(dapcomm,varainfo,varainfo->cache->datadds,data);
417    if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto fail;}
418
419fail:
420    if(vars != NULLnclistfree(vars);
421    if(varaprojection != NULLdcefree((DCEnode*)varaprojection);
422    if(fetchconstraint != NULLdcefree((DCEnode*)fetchconstraint);
423    if(varainfo != NULLfreegetvara(varainfo);
424    if(ocstat != OC_NOERRncstat = ocerrtoncerr(ocstat);
425    return THROW(ncstat);
426}
427
428/* Remove any pseudodimensions (sequence and string)*/
429static NCerror
430removepseudodims(DCEprojectionproj)
431{
432    int i;
433#ifdef DEBUG1
434fprintf(stderr,"removesequencedims.before: %s\n",dumpprojection(proj));
435#endif
436    for(i=0;i<nclistlength(proj->var->segments);i++) {
437 DCEsegmentseg = (DCEsegment*)nclistget(proj->var->segments,i);
438 CDFnodecdfnode = (CDFnode*)seg->annotation;
439 if(cdfnode->array.seqdim != NULL)
440     seg->rank = 0;
441 else if(cdfnode->array.stringdim != NULL)
442     seg->rank--;
443    }
444#ifdef DEBUG1
445fprintf(stderr,"removepseudodims.after: %s\n",dumpprojection(proj));
446#endif
447    return NC_NOERR;
448}
449
450static NCerror
451moveto(NCDAPCOMMONnccommGetvaraxgetvarCDFnodexrootnode, void* memory)
452{
453    OCerror ocstat = OC_NOERR;
454    NCerror ncstat = NC_NOERR;
455    OClink conn = nccomm->oc.conn;
456    OCdatanode xrootcontent;
457    OCddsnode ocroot;
458    NClistpath = nclistnew();
459    struct NCMEMORY memstate;
460
461    memstate.next = (memstate.memory = memory);
462
463    /* Get the root content*/
464    ocroot = xrootnode->tree->ocroot;
465    ocstat = oc_data_getroot(conn,ocroot,&xrootcontent);
466    if(ocstat != OC_NOERR) {THROWCHK(ocstat); goto done;}
467
468    /* Remember: xgetvar->target is in DATADDS tree */
469    collectnodepath(xgetvar->target,path,WITHDATASET);
470    ncstat = movetor(nccomm,xrootcontent,
471                     path,0,xgetvar,0,&memstate,
472                     xgetvar->varaprojection->var->segments);
473done:
474    nclistfree(path);
475    oc_data_free(conn,xrootcontent);
476    if(ocstat != OC_NOERRncstat = ocerrtoncerr(ocstat);
477    return THROW(ncstat);
478}
479
480static NCerror
481movetor(NCDAPCOMMONnccomm,
482 OCdatanode currentcontent,
483 NClistpath,
484        int depth, /* depth is position in segment list*/
485 Getvaraxgetvar,
486        size_t dimindex, /* dimindex is position in xgetvar->slices*/
487 struct NCMEMORYmemory,
488 NClistsegments)
489{
490    OCerror ocstat = OC_NOERR;
491    NCerror ncstat = NC_NOERR;
492    OClink conn = nccomm->oc.conn;
493    CDFnodexnode = (CDFnode*)nclistget(path,depth);
494    OCdatanode reccontent = NULL;
495    OCdatanode dimcontent = NULL;
496    OCdatanode fieldcontent = NULL;
497    Dapodometerodom = NULL;
498    int hasstringdim = 0;
499    DCEsegmentsegment;
500    OCDT mode;
501
502    /* Note that we use depth-1 because the path contains the DATASET
503       but the segment list does not */
504    segment = (DCEsegment*)nclistget(segments,depth-1); /*may be NULL*/
505    if(xnode->etype == NC_STRING || xnode->etype == NC_URLhasstringdim = 1;
506
507    /* Get the mode */
508    mode = oc_data_mode(conn,currentcontent);
509
510#ifdef DEBUG2
511fprintf(stderr,"moveto: nctype=%d depth=%d dimindex=%d mode=%s",
512        xnode->nctypedepth,dimindex,oc_data_modestring(mode));
513fprintf(stderr," segment=%s hasstringdim=%d\n",
514 dcetostring((DCEnode*)segment),hasstringdim);
515#endif
516
517    switch (xnode->nctype) {
518
519#if 0
520#define OCDT_FIELD     ((OCDT)(1)) /* field of a container */
521#define OCDT_ELEMENT   ((OCDT)(2)) /* element of a structure array */
522#define OCDT_RECORD    ((OCDT)(4)) /* record of a sequence */
523#define OCDT_ARRAY     ((OCDT)(8)) /* is structure array */
524#define OCDT_SEQUENCE  ((OCDT)(16)) /* is sequence */
525#define OCDT_ATOMIC    ((OCDT)(32)) /* is atomic leaf */
526#endif
527
528    default:
529 goto done;
530
531    case NC_Grid:
532    case NC_Dataset:
533    case NC_Structure:
534 /* Separate out the case where structure is dimensioned */
535 if(oc_data_indexable(conn,currentcontent)) {
536     /* => dimensioned structure */
537            /* The current segment should reflect the
538        proper parts of the nc_get_vara argument
539     */
540     /* Create odometer for this structure's part of the projection */
541            odom = dapodom_fromsegment(segment,0,segment->rank);
542            while(dapodom_more(odom)) {
543                /* Compute which instance to move to*/
544                ocstat = oc_data_ithelement(conn,currentcontent,
545                                            odom->index,&dimcontent);
546                if(ocstat != OC_NOERR) {THROWCHK(ocstat); goto done;}
547                ASSERT(oc_data_indexed(conn,dimcontent));
548                ncstat = movetor(nccomm,dimcontent,
549                                 path,depth,/*keep same depth*/
550                                 xgetvar,dimindex+segment->rank,
551                                 memory,segments);
552                dapodom_next(odom);
553            }
554            dapodom_free(odom);
555     odom = NULL;
556 } else {/* scalar instance */
557     ncstat = movetofield(nccomm,currentcontent,path,depth,xgetvar,dimindex,memory,segments);
558     if(ocstat != OC_NOERR) {THROWCHK(ocstat); goto done;}
559 } break;
560
561    case NC_Sequence:
562 if(fIsSet(mode,OCDT_SEQUENCE)) {
563            ASSERT((xnode->attachment != NULL));
564            ASSERT((segment != NULL));
565     ASSERT((segment->rank == 1));
566     /* Build an odometer for walking the sequence,
567               however, watch out
568               for the case when the user set a limit and that limit
569               is not actually reached in this request.
570            */
571            /* By construction, this sequence represents the first
572               (and only) dimension of this segment */
573            odom = dapodom_fromsegment(segment,0,1);
574     while(dapodom_more(odom)) {
575 size_t recordindex = dapodom_count(odom);
576                ocstat = oc_data_ithrecord(conn,currentcontent,
577    recordindex,&reccontent);
578                if(ocstat != OC_NOERR) {
579     if(ocstat == OC_EINDEX)
580 ocstat = OC_EINVALCOORDS;
581     THROWCHK(ocstat); goto done;
582 }
583                ncstat = movetor(nccomm,reccontent,
584                                     path,depth,
585                                     xgetvar,dimindex+1,
586                                     memory,segments);
587                if(ncstat != OC_NOERR) {THROWCHK(ncstat); goto done;}
588 dapodom_next(odom);
589            }
590 } else if(fIsSet(mode,OCDT_RECORD)) {
591     /* Treat like structure */
592     /* currentcontent points to the record instance */
593     ncstat = movetofield(nccomm,currentcontent,path,depth,xgetvar,dimindex,memory,segments);
594     if(ocstat != OC_NOERR) {THROWCHK(ocstat); goto done;}
595 }
596 break;
597
598    case NC_Atomic:
599
600        if(hasstringdim)
601     ncstat = extractstring(nccommxgetvarxnodesegmentdimindexconncurrentcontentmemory);
602 else
603     ncstat = extract(nccommxgetvarxnodesegmentdimindexconncurrentcontentmemory);
604 break;
605
606    }
607
608done:
609    oc_data_free(conn,dimcontent);
610    oc_data_free(conn,fieldcontent);
611    oc_data_free(conn,reccontent);
612    if(ocstat != OC_NOERRncstat = ocerrtoncerr(ocstat);
613    if(odomdapodom_free(odom);
614    return THROW(ncstat);
615}
616
617static NCerror
618movetofield(NCDAPCOMMONnccomm,
619 OCdatanode currentcontent,
620 NClistpath,
621        int depth, /* depth is position in segment list*/
622 Getvaraxgetvar,
623        size_t dimindex, /* dimindex is position in xgetvar->slices*/
624 struct NCMEMORYmemory,
625 NClistsegments)
626{
627    OCerror ocstat = OC_NOERR;
628    NCerror ncstat = NC_NOERR;
629    size_t fieldindex,gridindex;
630    OClink conn = nccomm->oc.conn;
631    CDFnodexnode = (CDFnode*)nclistget(path,depth);
632    OCdatanode reccontent = NULL;
633    OCdatanode dimcontent = NULL;
634    OCdatanode fieldcontent = NULL;
635    CDFnodexnext;
636    int newdepth;
637    int ffield;
638
639    /* currentcontent points to the grid/dataset/structure/record instance */
640    xnext = (CDFnode*)nclistget(path,depth+1);
641    ASSERT((xnext != NULL));
642
643     /* If findfield is less than 0,
644         and passes through this stanza,
645         an undefined value will be passed to
646         oc_data_ithfield.  See coverity
647         issue 712596. */
648    ffield = findfield(xnodexnext);
649    if(ffield < 0) {
650      ncstat = NC_EBADFIELD;
651      goto done;
652    } else {
653      fieldindex = findfield(xnode,xnext);
654    }
655
656    /* If the next node is a nc_virtual node, then
657       we need to effectively
658       ignore it and use the appropriate subnode.
659       If the next node is a re-struct'd node, then
660       use it as is.
661    */
662    if(xnext->nc_virtual) {
663        CDFnodexgrid = xnext;
664 xnext = (CDFnode*)nclistget(path,depth+2); /* real node */
665        gridindex = fieldindex;
666 fieldindex = findfield(xgrid,xnext);
667 fieldindex += gridindex;
668 newdepth = depth+2;
669    } else {
670        newdepth = depth+1;
671    }
672    /* Move to appropriate field */
673    ocstat = oc_data_ithfield(conn,currentcontent,fieldindex,&fieldcontent);
674    if(ocstat != OC_NOERR) {THROWCHK(ocstat); goto done;}
675    ncstat = movetor(nccomm,fieldcontent,
676                     path,newdepth,xgetvar,dimindex,memory,
677      segments);
678
679done:
680    oc_data_free(conn,dimcontent);
681    oc_data_free(conn,fieldcontent);
682    oc_data_free(conn,reccontent);
683    if(ocstat != OC_NOERRncstat = ocerrtoncerr(ocstat);
684    return THROW(ncstat);
685}
686
687#if 0
688
689/* Determine the index in the odometer at which
690   the odometer will be walking the whole subslice
691   This will allow us to optimize.
692*/
693static int
694wholeslicepoint(Dapodometerodom)
695{
696    unsigned int i;
697    int point;
698    for(point=-1,i=0;i<odom->rank;i++) {
699        ASSERT((odom->size[i] != 0));
700        if(odom->start[i] != 0 || odom->stride[i] != 1
701           || odom->count[i] != odom->size[i])
702     point = i;
703    }
704    if(point == -1)
705 point = 0; /* wholevariable */
706    else if(point == (odom->rank - 1))
707 point = -1; /* no whole point */
708    else
709 point += 1; /* intermediate point */
710    return point;
711}
712#endif
713
714static int
715findfield(CDFnodenodeCDFnodefield)
716{
717    size_t i;
718    for(i=0;i<nclistlength(node->subnodes);i++) {
719        CDFnodetest = (CDFnode*) nclistget(node->subnodes,i);
720        if(test == field) return i;
721    }
722    return -1;
723}
724
725static int
726conversionrequired(nc_type t1nc_type t2)
727{
728    if(t1 == t2)
729 return 0;
730    if(nctypesizeof(t1) != nctypesizeof(t2))
731 return 1;
732    /* Avoid too many cases by making t1 < t2 */
733    if(t1 > t2) {int tmp = t1t1 = t2t2 = tmp;}
734#undef CASE
735#define CASE(t1,t2) ((t1)<<5 | (t2))
736    switch (CASE(t1,t2)) {
737    case CASE(NC_BYTE,NC_UBYTE):
738    case CASE(NC_BYTE,NC_CHAR):
739    case CASE(NC_CHAR,NC_UBYTE):
740    case CASE(NC_SHORT,NC_USHORT):
741    case CASE(NC_INT,NC_UINT):
742    case CASE(NC_INT64,NC_UINT64):
743 return 0;
744    default: break;
745    }
746    return 1;
747}
748
749/* We are at a primitive variable or scalar that has no string dimensions.
750   Extract the data. (This is way too complicated)
751*/
752static int
753extract(
754 NCDAPCOMMONnccomm,
755 Getvaraxgetvar,
756 CDFnodexnode,
757        DCEsegmentsegment,
758 size_t dimindex,/*notused*/
759        OClink conn,
760        OCdatanode currentcontent,
761 struct NCMEMORYmemory
762       )
763{
764    OCerror ocstat = OC_NOERR;
765    NCerror ncstat = NC_NOERR;
766    size_t count,rank0;
767    Dapodometerodom = NULL;
768    size_t externtypesize;
769    size_t interntypesize;
770    int requireconversion;
771    char value[16];
772
773    ASSERT((segment != NULL));
774
775    requireconversion = conversionrequired(xgetvar->dsttype,xnode->etype);
776
777    ASSERT(xgetvar->cache != NULL);
778    externtypesize = nctypesizeof(xgetvar->dsttype);
779    interntypesize = nctypesizeof(xnode->etype);
780
781    rank0 = nclistlength(xnode->array.dimset0);
782
783#ifdef DEBUG2
784fprintf(stderr,"moveto: primitive: segment=%s",
785                dcetostring((DCEnode*)segment));
786fprintf(stderr," iswholevariable=%d",xgetvar->cache->wholevariable);
787fprintf(stderr,"\n");
788#endif
789
790    if(rank0 == 0) {/* scalar */
791 char* mem = (requireconversion?value:memory->next);
792 ASSERT(externtypesize <= sizeof(value));
793 /* Read the whole scalar directly into memory  */
794 ocstat = oc_data_readscalar(conn,currentcontent,externtypesize,mem);
795 if(ocstat != OC_NOERR) {THROWCHK(ocstat); goto done;}
796 if(requireconversion) {
797     /* convert the value to external type */
798            ncstat = dapconvert(xnode->etype,xgetvar->dsttype,memory->next,value,1);
799            if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
800        }
801        memory->next += (externtypesize);
802    } else if(xgetvar->cache->wholevariable) {/* && rank0 > 0 */
803 /* There are multiple cases, assuming no conversion required.
804           1) client is asking for whole variable
805              => start=0, count=totalsize, stride=1
806       => read whole thing at one shot
807           2) client is asking for non-strided subset
808              and edges are maximal
809              => start=x, count=y, stride=1
810       => read whole subset at one shot
811           3) client is asking for strided subset or edges are not maximal
812              => start=x, count=y, stride=s
813       => we have to use odometer on leading prefix.
814           If conversion required, then read one-by-one
815 */
816 int safeindex = dcesafeindex(segment,0,rank0);
817 assert(safeindex >= 0 && safeindex <= rank0);
818
819 if(!requireconversion && safeindex == 0) { /* can read whole thing */
820            size_t internlen;
821     count = dcesegmentsize(segment,0,rank0); /* how many to read */
822     internlen = interntypesize*count;
823            /* Read the whole variable directly into memory.*/
824            ocstat = oc_data_readn(conn,currentcontent,dap_zero,count,internlen,memory->next);
825     /* bump memory pointer */
826     memory->next += internlen;
827            if(ocstat != OC_NOERR) {THROWCHK(ocstat); goto done;}
828 } else if(!requireconversion && safeindex > 0 && safeindex < rank0) {
829     size_t internlen;
830     /* We need to build an odometer for the unsafe prefix of the slices */
831     odom = dapodom_fromsegment(segment,0,safeindex);
832     count = dcesegmentsize(segment,safeindex,rank0); /* read in count chunks */
833     internlen = interntypesize*count;
834     while(dapodom_more(odom)) {
835         ocstat = oc_data_readn(conn,currentcontent,odom->index,count,internlen,memory->next);
836         if(ocstat != OC_NOERR) {THROWCHK(ocstat); goto done;}
837         memory->next += internlen;
838         dapodom_next(odom);
839     }
840            dapodom_free(odom);
841        } else {
842      /* Cover following cases, all of which require reading
843                values one-by-one:
844 1. requireconversion
845 2. !requireconversion but safeindex == rank0 =>no safe indices
846 Note that in case 2, we will do a no-op conversion.
847      */
848            odom = dapodom_fromsegment(segment,0,rank0);
849     while(dapodom_more(odom)) {
850         char value[16]; /* Big enough to hold any numeric value */
851         ocstat = oc_data_readn(conn,currentcontent,odom->index,1,interntypesize,value);
852         if(ocstat != OC_NOERR) {THROWCHK(ocstat); goto done;}
853         ncstat = dapconvert(xnode->etype,xgetvar->dsttype,memory->next,value,1);
854         if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
855         memory->next += (externtypesize);
856         dapodom_next(odom);
857     }
858            dapodom_free(odom);
859        }
860    } else { /* !xgetvar->cache->wholevariable && rank0 > 0 */
861 /* This is the case where the constraint was applied by the server,
862           so we just read it in, possibly with conversion
863 */
864 if(requireconversion) {
865     /* read one-by-one */
866            odom = dapodom_fromsegment(segment,0,rank0);
867     while(dapodom_more(odom)) {
868         char value[16]; /* Big enough to hold any numeric value */
869         ocstat = oc_data_readn(conn,currentcontent,odom->index,1,interntypesize,value);
870         if(ocstat != OC_NOERR) {THROWCHK(ocstat); goto done;}
871         ncstat = dapconvert(xnode->etype,xgetvar->dsttype,memory->next,value,1);
872         if(ncstat != NC_NOERR) {THROWCHK(ncstat); goto done;}
873         memory->next += (externtypesize);
874         dapodom_next(odom);
875     }
876            dapodom_free(odom);
877 } else {/* Read straight to memory */
878            size_t internlen;
879     count = dcesegmentsize(segment,0,rank0); /* how many to read */
880     internlen = interntypesize*count;
881            ocstat = oc_data_readn(conn,currentcontent,dap_zero,count,internlen,memory->next);
882            if(ocstat != OC_NOERR) {THROWCHK(ocstat); goto done;}
883 }
884    }
885done:
886    return THROW(ncstat);
887}
888
889static NCerror
890slicestring(OClink conn, char* stringmemDCEsliceslice, struct NCMEMORYmemory)
891{
892    size_t stringlen;
893    unsigned int i;
894    NCerror ncstat = NC_NOERR;
895    char* lastchar;
896    size_t charcount; /* number of characters inserted into memory */
897
898    /* libnc-dap chooses to convert string escapes to the corresponding
899       character; so we do likewise.
900    */
901    dapexpandescapes(stringmem);
902    stringlen = strlen(stringmem);
903
904#ifdef DEBUG2
905fprintf(stderr,"moveto: slicestring: string/%lu=%s\n",stringlen,stringmem);
906fprintf(stderr,"slicestring: %lu string=|%s|\n",stringlen,stringmem);
907fprintf(stderr,"slicestring: slice=[%lu:%lu:%lu/%lu]\n",
908slice->first,slice->stride,slice->stop,slice->declsize);
909#endif
910
911    /* Stride across string; if we go past end of string, then pad*/
912    charcount = 0;
913    for(i=slice->first;i<slice->length;i+=slice->stride) {
914        if(i < stringlen)
915            *memory->next = stringmem[i];
916        else /* i >= stringlen*/
917            *memory->next = NC_FILL_CHAR;
918 memory->next++;
919 charcount++;
920    }
921    lastchar = (memory->next);
922    if(charcount > 0) {
923        lastchar--;
924    }
925
926    return THROW(ncstat);
927}
928
929/*
930Extract data for a netcdf variable that has a string dimension.
931*/
932static int
933extractstring(
934 NCDAPCOMMONnccomm,
935 Getvaraxgetvar,
936 CDFnodexnode,
937        DCEsegmentsegment,
938 size_t dimindex, /*notused*/
939        OClink conn,
940        OCdatanode currentcontent,
941 struct NCMEMORYmemory
942       )
943{
944    NCerror ncstat = NC_NOERR;
945    OCerror ocstat = OC_NOERR;
946    int i;
947    size_t rank0;
948    NCliststrings = NULL;
949    Dapodometerodom = NULL;
950
951    ASSERT(xnode->etype == NC_STRING || xnode->etype == NC_URL);
952
953    /* Compute rank minus string dimension */
954    rank0 = nclistlength(xnode->array.dimset0);
955
956    /* keep whole extracted strings stored in an NClist */
957    strings = nclistnew();
958
959    if(rank0 == 0) {/*=> scalar*/
960 char* value = NULL;
961 ocstat = oc_data_readscalar(conn,currentcontent,sizeof(value),&value);
962 if(ocstat != OC_NOERR) goto done;
963 nclistpush(strings,(void*)value);
964    } else {
965        /* Use the odometer to walk to the appropriate fields*/
966        odom = dapodom_fromsegment(segment,0,rank0);
967        while(dapodom_more(odom)) {
968     char* value = NULL;
969     ocstat = oc_data_readn(conn,currentcontent,odom->index,1,sizeof(value),&value);
970     if(ocstat != OC_NOERR)
971 goto done;
972     nclistpush(strings,(void*)value);
973            dapodom_next(odom);
974 }
975        dapodom_free(odom);
976 odom = NULL;
977    }
978    /* Get each string in turn, slice it by applying the string dimm
979       and store in user supplied memory
980    */
981    for(i=0;i<nclistlength(strings);i++) {
982 char* s = (char*)nclistget(strings,i);
983 slicestring(conn,s,&segment->slices[rank0],memory);
984 free(s);
985    }
986done:
987    if(strings != NULLnclistfree(strings);
988    if(ocstat != OC_NOERRncstat = ocerrtoncerr(ocstat);
989    return THROW(ncstat);
990}
991
992static NCerror
993makegetvar(NCDAPCOMMONnccommCDFnodevar, void* datanc_type dsttypeGetvara** getvarp)
994{
995    NCerror ncstat = NC_NOERR;
996
997    if(getvarp)
998    {
999       Getvaragetvar;
1000
1001       getvar = (Getvara*)calloc(1,sizeof(Getvara));
1002       MEMCHECK(getvar,NC_ENOMEM);
1003
1004       getvar->target = var;
1005       getvar->memory = data;
1006       getvar->dsttype = dsttype;
1007
1008       *getvarp = getvar;
1009    }
1010    return ncstat;
1011}
1012
1013/*
1014Given DDS node, locate the node
1015in a DATADDS that matches the DDS node.
1016Return NULL if no node found
1017*/
1018
1019void
1020unattach(CDFnoderoot)
1021{
1022    unsigned int i;
1023    CDFtreextree = root->tree;
1024    for(i=0;i<nclistlength(xtree->nodes);i++) {
1025 CDFnodexnode = (CDFnode*)nclistget(xtree->nodes,i);
1026 /* break bi-directional link */
1027        xnode->attachment = NULL;
1028    }
1029}
1030
1031static void
1032setattach(CDFnodetargetCDFnodepattern)
1033{
1034    target->attachment = pattern;
1035    pattern->attachment = target;
1036    /* Transfer important information */
1037    target->externaltype = pattern->externaltype;
1038    target->maxstringlength = pattern->maxstringlength;
1039    target->sequencelimit = pattern->sequencelimit;
1040    target->ncid = pattern->ncid;
1041    /* also transfer libncdap4 info */
1042    target->typeid = pattern->typeid;
1043    target->typesize = pattern->typesize;
1044}
1045
1046static NCerror
1047attachdims(CDFnodexnodeCDFnodepattern)
1048{
1049    unsigned int i;
1050    for(i=0;i<nclistlength(xnode->array.dimsetall);i++) {
1051 CDFnodexdim = (CDFnode*)nclistget(xnode->array.dimsetall,i);
1052 CDFnodetdim = (CDFnode*)nclistget(pattern->array.dimsetall,i);
1053 setattach(xdim,tdim);
1054#ifdef DEBUG2
1055fprintf(stderr,"attachdim: %s->%s\n",xdim->ocname,tdim->ocname);
1056#endif
1057    }
1058    return NC_NOERR;
1059}
1060
1061/*
1062Match a DATADDS node to a DDS node.
1063It is assumed that both trees have been re-struct'ed if necessary.
1064*/
1065
1066static NCerror
1067attachr(CDFnodexnodeNClistpatternpath, int depth)
1068{
1069    unsigned int i,plen,lastnode,gridable;
1070    NCerror ncstat = NC_NOERR;
1071    CDFnodepatternpathnode;
1072    CDFnodepatternpathnext;
1073
1074    plen = nclistlength(patternpath);
1075    if(depth >= plen) {THROWCHK(ncstat=NC_EINVAL); goto done;}
1076
1077    lastnode = (depth == (plen-1));
1078    patternpathnode = (CDFnode*)nclistget(patternpath,depth);
1079    ASSERT((simplenodematch(xnode,patternpathnode)));
1080    setattach(xnode,patternpathnode);
1081#ifdef DEBUG2
1082fprintf(stderr,"attachnode: %s->%s\n",xnode->ocname,patternpathnode->ocname);
1083#endif
1084
1085    if(lastnode) goto done; /* We have the match and are done */
1086
1087    if(nclistlength(xnode->array.dimsetall) > 0) {
1088 attachdims(xnode,patternpathnode);
1089    }
1090
1091    ASSERT((!lastnode));
1092    patternpathnext = (CDFnode*)nclistget(patternpath,depth+1);
1093
1094    gridable = (patternpathnext->nctype == NC_Grid && depth+2 < plen);
1095
1096    /* Try to find an xnode subnode that matches patternpathnext */
1097    for(i=0;i<nclistlength(xnode->subnodes);i++) {
1098        CDFnodexsubnode = (CDFnode*)nclistget(xnode->subnodes,i);
1099        if(simplenodematch(xsubnode,patternpathnext)) {
1100     ncstat = attachr(xsubnode,patternpath,depth+1);
1101     if(ncstat) goto done;
1102        } else if(gridable && xsubnode->nctype == NC_Atomic) {
1103            /* grids may or may not appear in the datadds;
1104        try to match the xnode subnodes against the parts of the grid
1105     */
1106        CDFnodepatternpathnext2 = (CDFnode*)nclistget(patternpath,depth+2);
1107     if(simplenodematch(xsubnode,patternpathnext2)) {
1108         ncstat = attachr(xsubnode,patternpath,depth+2);
1109                if(ncstat) goto done;
1110     }
1111 }
1112    }
1113done:
1114    return THROW(ncstat);
1115}
1116
1117NCerror
1118attach(CDFnodexrootCDFnodepattern)
1119{
1120    NCerror ncstat = NC_NOERR;
1121    NClistpatternpath = nclistnew();
1122    CDFnodeddsroot = pattern->root;
1123
1124    if(xroot->attachmentunattach(xroot);
1125    if(ddsroot != NULL && ddsroot->attachmentunattach(ddsroot);
1126    if(!simplenodematch(xroot,ddsroot))
1127 {THROWCHK(ncstat=NC_EINVAL); goto done;}
1128    collectnodepath(pattern,patternpath,WITHDATASET);
1129    ncstat = attachr(xroot,patternpath,0);
1130done:
1131    nclistfree(patternpath);
1132    return ncstat;
1133}
1134
1135static NCerror
1136attachsubsetr(CDFnodetargetCDFnodepattern)
1137{
1138    unsigned int i;
1139    NCerror ncstat = NC_NOERR;
1140    int fieldindex;
1141
1142#ifdef DEBUG2
1143fprintf(stderr,"attachsubsetr: attach: target=%s pattern=%s\n",
1144 target->ocname,pattern->ocname);
1145#endif
1146
1147    ASSERT((nodematch(target,pattern)));
1148    setattach(target,pattern);
1149
1150    /* Try to match target subnodes against pattern subnodes */
1151
1152    fieldindex = 0;
1153    for(fieldindex=0,i=0;i<nclistlength(pattern->subnodes) && fieldindex<nclistlength(target->subnodes);i++) {
1154        CDFnodepatternsubnode = (CDFnode*)nclistget(pattern->subnodes,i);
1155        CDFnodetargetsubnode = (CDFnode*)nclistget(target->subnodes,fieldindex);
1156        if(nodematch(targetsubnode,patternsubnode)) {
1157#ifdef DEBUG2
1158fprintf(stderr,"attachsubsetr: match: %s :: %s\n",targetsubnode->ocname,patternsubnode->ocname);
1159#endif
1160            ncstat = attachsubsetr(targetsubnode,patternsubnode);
1161        if(ncstat) goto done;
1162     fieldindex++;
1163 }
1164    }
1165done:
1166    return THROW(ncstat);
1167}
1168
1169
1170/*
1171Match nodes in pattern tree to nodes in target tree;
1172pattern tree is typically a structural superset of target tree.
1173WARNING: Dimensions are not attached
1174*/
1175
1176static NCerror
1177attachsubset(CDFnodetargetCDFnodepattern)
1178{
1179    NCerror ncstat = NC_NOERR;
1180
1181    if(pattern == NULL) {THROWCHK(ncstat=NC_NOERR); goto done;}
1182    if(!nodematch(target,pattern)) {THROWCHK(ncstat=NC_EINVAL); goto done;}
1183#ifdef DEBUG2
1184fprintf(stderr,"attachsubset: target=%s\n",dumptree(target));
1185fprintf(stderr,"attachsubset: pattern=%s\n",dumptree(pattern));
1186#endif
1187    ncstat = attachsubsetr(target,pattern);
1188done:
1189    return ncstat;
1190}
1191
1192static void
1193freegetvara(Getvaravara)
1194{
1195    if(vara == NULL) return;
1196    dcefree((DCEnode*)vara->varaprojection);
1197    nullfree(vara);
1198}
1199
1200#ifdef EXTERN_UNUSED
1201int
1202nc3d_getvarmx(int ncid, int varid,
1203     const size_t *start,
1204     const size_t *edges,
1205     const ptrdiff_tstride,
1206      const ptrdiff_tmap,
1207     void* data,
1208     nc_type dsttype0)
1209{
1210    NCerror ncstat = NC_NOERR;
1211    int i;
1212    NCdrno;
1213    NCsubstrate;
1214    NC3_INFOsubstrate3;
1215    NCDAPCOMMONdapcomm;
1216    NC_varvar;
1217    CDFnodecdfvar; /* cdf node mapping to var*/
1218    NClistvarnodes;
1219    nc_type dsttype;
1220    size_t externsize;
1221    size_t dimsizes[NC_MAX_VAR_DIMS];
1222    Dapodometerodom = NULL;
1223    unsigned int ncrank;
1224    NClistncdims = NULL;
1225    size_t nelems;
1226#ifdef NEWVARM
1227    char* localcopy; /* of whole variable */
1228#endif
1229
1230    ncstat = NC_check_id(ncid, (NC**)&drno);
1231    if(ncstat != NC_NOERR) goto done;
1232    dapcomm = (NCDAPCOMMON*)drno->dispatchdata;
1233
1234    ncstat = NC_check_id(drno->substrate, &substrate);
1235    if(ncstat != NC_NOERR) goto done;
1236    substrate3 = (NC3_INFO*)drno->dispatchdata;
1237
1238    var = NC_lookupvar(substrate3,varid);
1239    if(var == NULL) {ncstat = NC_ENOTVAR; goto done;}
1240
1241    /* Locate var node via varid */
1242    varnodes = dapcomm->cdf.ddsroot->tree->varnodes;
1243    for(i=0;i<nclistlength(varnodes);i++) {
1244 CDFnodenode = (CDFnode*)nclistget(varnodes,i);
1245 if(node->array.basevar == NULL
1246           && node->nctype == NC_Atomic
1247           && node->ncid == varid) {
1248     cdfvar = node;
1249     break;
1250 }
1251    }
1252
1253    ASSERT((cdfvar != NULL));
1254    ASSERT((strcmp(cdfvar->ncfullname,var->name->cp)==0));
1255
1256    if(nclistlength(cdfvar->array.dimsetplus) == 0) {
1257       /* The variable is a scalar; consequently, there is only one
1258          thing to get and only one place to put it. */
1259 /* recurse with additional parameters */
1260        return THROW(nc3d_getvarx(ncid,varid,
1261  NULL,NULL,NULL,
1262  data,dsttype0));
1263    }
1264
1265    dsttype = (dsttype0);
1266
1267    /* Default to using the inquiry type for this var*/
1268    if(dsttype == NC_NATdsttype = cdfvar->externaltype;
1269
1270    /* Validate any implied type conversion*/
1271    if(cdfvar->etype != dsttype && dsttype == NC_CHAR) {
1272 /* The only disallowed conversion is to/from char and non-byte
1273           numeric types*/
1274 switch (cdfvar->etype) {
1275 case NC_STRING: case NC_URL:
1276 case NC_CHAR: case NC_BYTE: case NC_UBYTE:
1277      break;
1278 default:
1279     return THROW(NC_ECHAR);
1280 }
1281    }
1282
1283    externsize = nctypesizeof(dsttype);
1284
1285    /* Accumulate the dimension sizes and the total # of elements */
1286    ncdims = cdfvar->array.dimsetall;
1287    ncrank = nclistlength(ncdims);
1288
1289    nelems = 1; /* also Compute the number of elements being retrieved */
1290    for(i=0;i<ncrank;i++) {
1291 CDFnodedim = (CDFnode*)nclistget(ncdims,i);
1292 dimsizes[i] = dim->dim.declsize;
1293 nelems *= edges[i];
1294    }
1295
1296    /* Originally, this code repeatedly extracted single values
1297       using get_var1. In an attempt to improve performance,
1298       I have converted to reading the whole variable at once
1299       and walking it locally.
1300    */
1301
1302#ifdef NEWVARM
1303    localcopy = (char*)malloc(nelems*externsize);
1304
1305    /* We need to use the varieties of get_vars in order to
1306       properly do conversion to the external type
1307    */
1308
1309    switch (dsttype) {
1310
1311    case NC_CHAR:
1312 ncstat = nc_get_vars_text(ncid,varid,startedgesstride,
1313   (char*)localcopy);
1314 break;
1315    case NC_BYTE:
1316 ncstat = nc_get_vars_schar(ncid,varid,startedgesstride,
1317    (signed char*)localcopy);
1318 break;
1319    case NC_SHORT:
1320 ncstat = nc_get_vars_short(ncid,varidstartedgesstride,
1321       (short*)localcopy);
1322 break;
1323    case NC_INT:
1324 ncstat = nc_get_vars_int(ncid,varid,startedgesstride,
1325  (int*)localcopy);
1326 break;
1327    case NC_FLOAT:
1328 ncstat = nc_get_vars_float(ncid,varid,startedgesstride,
1329    (float*)localcopy);
1330 break;
1331    case NC_DOUBLE:
1332 ncstat = nc_get_vars_double(ncid,varid, startedgesstride,
1333       (double*)localcopy);
1334 break;
1335    default: break;
1336    }
1337
1338
1339    odom = dapodom_new(ncrank,start,edges,stride,NULL);
1340
1341    /* Walk the local copy */
1342    for(i=0;i<nelems;i++) {
1343 size_t voffset = dapodom_varmcount(odom,map,dimsizes);
1344 void* dataoffset = (void*)(((char*)data) + (externsize*voffset));
1345 char* localpos = (localcopy + externsize*i);
1346 /* extract the indexset'th value from local copy */
1347 memcpy(dataoffset,(void*)localpos,externsize);
1348/*
1349fprintf(stderr,"new: %lu -> %lu  %f\n",
1350 (unsigned long)(i),
1351        (unsigned long)voffset,
1352 *(float*)localpos);
1353*/
1354 dapodom_next(odom);
1355    }
1356#else
1357    odom = dapodom_new(ncrank,start,edges,stride,NULL);
1358    while(dapodom_more(odom)) {
1359 size_t* indexset = odom->index;
1360 size_t voffset = dapodom_varmcount(odom,map,dimsizes);
1361 char internalmem[128];
1362 char externalmem[128];
1363 void* dataoffset = (void*)(((char*)data) + (externsize*voffset));
1364
1365 /* get the indexset'th value using variable's internal type */
1366 ncstat = nc_get_var1(ncid,varid,indexset,(void*)&internalmem);
1367        if(ncstat != NC_NOERR) goto done;
1368 /* Convert to external type */
1369 ncstat = dapconvert(cdfvar->etype,dsttype,externalmem,internalmem);
1370        if(ncstat != NC_NOERR) goto done;
1371 memcpy(dataoffset,(void*)externalmem,externsize);
1372/*
1373fprintf(stderr,"old: %lu -> %lu  %f\n",
1374 (unsigned long)dapodom_count(odom),
1375        (unsigned long)voffset,
1376 *(float*)externalmem);
1377*/
1378 dapodom_next(odom);
1379    }
1380#endif
1381
1382done:
1383    return ncstat;
1384}
1385#endif /*EXTERN_UNUSED*/


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