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