1 /*********************************************************************
2  *   Copyright 2018, UCAR/Unidata
3  *   See netcdf/COPYRIGHT filey for copying and redistribution conditions.
4  *********************************************************************/
5 
6 #include "dapincludes.h"
7 #include "dceparselex.h"
8 #include "dceconstraints.h"
9 #include "dapdump.h"
10 
11 static void completesegments(NClist* fullpath, NClist* segments);
12 static NCerror qualifyprojectionnames(DCEprojection* proj);
13 static NCerror qualifyprojectionsizes(DCEprojection* proj);
14 static NCerror qualifyprojectionnames(DCEprojection* proj);
15 static NCerror matchpartialname(NClist* nodes, NClist* segments, CDFnode** nodep);
16 static int matchsuffix(NClist* matchpath, NClist* segments);
17 static int iscontainer(CDFnode* node);
18 static DCEprojection* projectify(CDFnode* field, DCEprojection* container);
19 static int slicematch(NClist* seglist1, NClist* seglist2);
20 
21 /* Parse incoming url constraints, if any,
22    to check for syntactic correctness */
23 NCerror
dapparsedapconstraints(NCDAPCOMMON * dapcomm,char * constraints,DCEconstraint * dceconstraint)24 dapparsedapconstraints(NCDAPCOMMON* dapcomm, char* constraints,
25 		    DCEconstraint* dceconstraint)
26 {
27     NCerror ncstat = NC_NOERR;
28     char* errmsg = NULL;
29 
30     ASSERT(dceconstraint != NULL);
31     nclistclear(dceconstraint->projections);
32     nclistclear(dceconstraint->selections);
33 
34     ncstat = dapceparse(constraints,dceconstraint,&errmsg);
35     if(ncstat) {
36       nclog(NCLOGWARN,"DCE constraint parse failure: %s",errmsg);
37       nclistclear(dceconstraint->projections);
38       nclistclear(dceconstraint->selections);
39     }
40     /* errmsg is freed whether ncstat or not. */
41     nullfree(errmsg);
42     return ncstat;
43 }
44 
45 /* Map constraint paths to CDFnode paths in specified tree and fill
46    in the declsizes.
47 
48    Two things to watch out for:
49    1. suffix paths are legal (i.e. incomplete paths)
50    2. nc_virtual nodes (via restruct)
51 
52 */
53 
54 NCerror
dapmapconstraints(DCEconstraint * constraint,CDFnode * root)55 dapmapconstraints(DCEconstraint* constraint,
56 		CDFnode* root)
57 {
58     size_t i;
59     NCerror ncstat = NC_NOERR;
60     NClist* nodes = root->tree->nodes;
61     NClist* dceprojections = constraint->projections;
62 
63     /* Convert the projection paths to leaves in the dds tree */
64     for(i=0;i<nclistlength(dceprojections);i++) {
65 	CDFnode* cdfmatch = NULL;
66 	DCEprojection* proj = (DCEprojection*)nclistget(dceprojections,i);
67 	if(proj->discrim != CES_VAR) continue; /* ignore functions */
68 	ncstat = matchpartialname(nodes,proj->var->segments,&cdfmatch);
69 	if(ncstat) goto done;
70 	/* Cross links */
71 	assert(cdfmatch != NULL);
72 	proj->var->annotation = (void*)cdfmatch;
73     }
74 
75 done:
76     return THROW(ncstat);
77 }
78 
79 
80 /* Fill in:
81     1. projection segments
82     2. projection segment slices declsize
83     3. selection path
84 */
85 NCerror
dapqualifyconstraints(DCEconstraint * constraint)86 dapqualifyconstraints(DCEconstraint* constraint)
87 {
88     NCerror ncstat = NC_NOERR;
89     size_t i;
90 #ifdef DEBUG
91 fprintf(stderr,"ncqualifyconstraints.before: %s\n",
92 		dumpconstraint(constraint));
93 #endif
94     if(constraint != NULL) {
95         for(i=0;i<nclistlength(constraint->projections);i++) {
96             DCEprojection* p = (DCEprojection*)nclistget(constraint->projections,i);
97             ncstat = qualifyprojectionnames(p);
98             ncstat = qualifyprojectionsizes(p);
99         }
100     }
101 #ifdef DEBUG
102 fprintf(stderr,"ncqualifyconstraints.after: %s\n",
103 		dumpconstraint(constraint));
104 #endif
105     return ncstat;
106 }
107 
108 /* convert all names in projections in paths to be fully qualified
109    by adding prefix segment objects.
110 */
111 static NCerror
qualifyprojectionnames(DCEprojection * proj)112 qualifyprojectionnames(DCEprojection* proj)
113 {
114     NCerror ncstat = NC_NOERR;
115     NClist* fullpath = nclistnew();
116 
117     ASSERT((proj->discrim == CES_VAR
118             && proj->var->annotation != NULL
119             && ((CDFnode*)proj->var->annotation)->ocnode != NULL));
120     collectnodepath((CDFnode*)proj->var->annotation,fullpath,!WITHDATASET);
121 #ifdef DEBUG
122 fprintf(stderr,"qualify: %s -> ",
123 	dumpprojection(proj));
124 #endif
125     /* Now add path nodes to create full path */
126     completesegments(fullpath,proj->var->segments);
127 
128 #ifdef DEBUG
129 fprintf(stderr,"%s\n",
130 	dumpprojection(proj));
131 #endif
132     nclistfree(fullpath);
133     return ncstat;
134 }
135 
136 /* Make sure that the slice declsizes are all defined for this projection */
137 static NCerror
qualifyprojectionsizes(DCEprojection * proj)138 qualifyprojectionsizes(DCEprojection* proj)
139 {
140     size_t i,j;
141     ASSERT(proj->discrim == CES_VAR);
142 #ifdef DEBUG
143 fprintf(stderr,"qualifyprojectionsizes.before: %s\n",
144 		dumpprojection(proj));
145 #endif
146     for(i=0;i<nclistlength(proj->var->segments);i++) {
147         DCEsegment* seg = (DCEsegment*)nclistget(proj->var->segments,i);
148 	NClist* dimset = NULL;
149 	CDFnode* cdfnode = (CDFnode*)seg->annotation;
150 	ASSERT(cdfnode != NULL);
151         dimset = cdfnode->array.dimsetplus;
152         seg->rank = nclistlength(dimset);
153 	/* For this, we do not want any string dimensions */
154 	if(cdfnode->array.stringdim != NULL) seg->rank--;
155         for(j=0;j<seg->rank;j++) {
156 	    CDFnode* dim = (CDFnode*)nclistget(dimset,j);
157 	    if(dim->dim.basedim != NULL) dim = dim->dim.basedim;
158             ASSERT(dim != null);
159 	    if(seg->slicesdefined)
160 	        seg->slices[j].declsize = dim->dim.declsize;
161 	    else
162 	        dcemakewholeslice(seg->slices+j,dim->dim.declsize);
163 	}
164         seg->slicesdefined = 1;
165         seg->slicesdeclized = 1;
166     }
167 #ifdef DEBUG
168 fprintf(stderr,"qualifyprojectionsizes.after: %s\n",
169 		dumpprojection(proj));
170 #endif
171     return NC_NOERR;
172 }
173 
174 static void
completesegments(NClist * fullpath,NClist * segments)175 completesegments(NClist* fullpath, NClist* segments)
176 {
177     size_t i,delta;
178     /* add path nodes to segments to create full path */
179     delta = (nclistlength(fullpath) - nclistlength(segments));
180     for(i=0;i<delta;i++) {
181         DCEsegment* seg = (DCEsegment*)dcecreate(CES_SEGMENT);
182         CDFnode* node = (CDFnode*)nclistget(fullpath,i);
183         seg->name = nulldup(node->ocname);
184         seg->annotation = (void*)node;
185 	seg->rank = nclistlength(node->array.dimset0);
186         nclistinsert(segments,i,(void*)seg);
187     }
188     /* Now modify the segments to point to the appropriate node
189        and fill in the slices.
190     */
191     for(i=delta;i<nclistlength(segments);i++) {
192         DCEsegment* seg = (DCEsegment*)nclistget(segments,i);
193         CDFnode* node = (CDFnode*)nclistget(fullpath,i);
194 	seg->annotation = (void*)node;
195     }
196 }
197 
198 /*
199 We are given a set of segments (in path)
200 representing a partial path for a CDFnode variable.
201 Our goal is to locate all matching
202 variables for which the path of that
203 variable has a suffix matching
204 the given partial path.
205 If one node matches exactly, then use that one;
206 otherwise there had better be exactly one
207 match else ambiguous.
208 Additional constraints (4/12/2010):
209 1. if a segment is dimensioned, then use that info
210    to distinguish e.g a grid node from a possible
211    grid array within it of the same name.
212    Treat sequences as of rank 1.
213 2. if there are two matches, and one is the grid
214    and the other is the grid array within that grid,
215    then choose the grid array.
216 3. If there are multiple matches choose the one with the
217    shortest path
218 4. otherwise complain about ambiguity
219 */
220 
221 /**
222  * Given a path as segments,
223  * try to locate the CDFnode
224  * instance (from a given set)
225  * that corresponds to the path.
226  * The key difficulty is that the
227  * path may only be a suffix of the
228  * complete path.
229  */
230 
231 static NCerror
matchpartialname(NClist * nodes,NClist * segments,CDFnode ** nodep)232 matchpartialname(NClist* nodes, NClist* segments, CDFnode** nodep)
233 {
234     size_t i,nsegs;
235     NCerror ncstat = NC_NOERR;
236     DCEsegment* lastseg = NULL;
237     NClist* namematches = nclistnew();
238     NClist* matches = nclistnew();
239     NClist* matchpath = nclistnew();
240 
241     /* Locate all nodes with the same name
242        as the last element in the segment path
243     */
244     nsegs = nclistlength(segments);
245     lastseg = (DCEsegment*)nclistget(segments,nsegs-1);
246     for(i=0;i<nclistlength(nodes);i++) {
247         CDFnode* node = (CDFnode*)nclistget(nodes,i);
248 	if(node->ocname == null)
249 	    continue;
250 	/* Path names come from oc space */
251         if(strcmp(node->ocname,lastseg->name) != 0)
252 	    continue;
253 	/* Only look at selected kinds of nodes */
254 	if(node->nctype != NC_Sequence
255                && node->nctype != NC_Structure
256                && node->nctype != NC_Grid
257                && node->nctype != NC_Atomic
258           )
259 	    continue;
260 	nclistpush(namematches,(void*)node);
261     }
262     if(nclistlength(namematches)==0) {
263         nclog(NCLOGERR,"No match for projection name: %s",lastseg->name);
264         ncstat = NC_EDDS;
265 	goto done;
266     }
267 
268     /* Now, collect and compare paths of the matching nodes */
269     for(i=0;i<nclistlength(namematches);i++) {
270         CDFnode* matchnode = (CDFnode*)nclistget(namematches,i);
271 	nclistclear(matchpath);
272 	collectnodepath(matchnode,matchpath,0);
273 	/* Do a suffix match */
274         if(matchsuffix(matchpath,segments)) {
275 	    nclistpush(matches,(void*)matchnode);
276 #ifdef DEBUG
277 fprintf(stderr,"matchpartialname: pathmatch: %s :: %s\n",
278 matchnode->ncfullname,dumpsegments(segments));
279 #endif
280 	}
281     }
282     /* |matches|==0 => no match; |matches|>1 => ambiguity */
283     switch (nclistlength(matches)) {
284     case 0:
285         nclog(NCLOGERR,"No match for projection name: %s",lastseg->name);
286         ncstat = NC_EDDS;
287 	break;
288     case 1:
289         if(nodep)
290 	    *nodep = (CDFnode*)nclistget(matches,0);
291 	break;
292     default: {
293 	CDFnode* minnode = NULL;
294 	int minpath = 0;
295 	int nmin = 0; /* to catch multiple ones with same short path */
296 	/* ok, see if one of the matches has a path that is shorter
297            then all the others */
298 	for(i=0;i<nclistlength(matches);i++) {
299 	    CDFnode* candidate = (CDFnode*)nclistget(matches,i);
300 	    nclistclear(matchpath);
301 	    collectnodepath(candidate,matchpath,0);
302 	    if(minpath == 0) {
303 		minpath = nclistlength(matchpath);
304 		minnode = candidate;
305 	    } else if(nclistlength(matchpath) == minpath) {
306 	        nmin++;
307 	    } else if(nclistlength(matchpath) < minpath) {
308 		minpath = nclistlength(matchpath);
309 		minnode = candidate;
310 		nmin = 1;
311 	    }
312 	} /*for*/
313 	if(minnode == NULL || nmin > 1) {
314 	    nclog(NCLOGERR,"Ambiguous match for projection name: %s",
315 			lastseg->name);
316             ncstat = NC_EDDS;
317 	} else if(nodep)
318 	    *nodep = minnode;
319 	} break;
320     }
321 #ifdef DEBUG
322 fprintf(stderr,"matchpartialname: choice: %s %s for %s\n",
323 (nclistlength(matches) > 1?"":"forced"),
324 (*nodep)->ncfullname,dumpsegments(segments));
325 #endif
326 
327 done:
328     nclistfree(namematches);
329     nclistfree(matches);
330     nclistfree(matchpath);
331     return THROW(ncstat);
332 }
333 
334 static int
matchsuffix(NClist * matchpath,NClist * segments)335 matchsuffix(NClist* matchpath, NClist* segments)
336 {
337     int i,pathstart;
338     int nsegs = nclistlength(segments);
339     int pathlen = nclistlength(matchpath);
340     int segmatch;
341 
342     /* try to match the segment list as a suffix of the path list */
343 
344     /* Find the maximal point in the path s.t. |suffix of path|
345        == |segments|
346     */
347     pathstart = (pathlen - nsegs);
348     if(pathstart < 0)
349 	return 0; /* pathlen <nsegs => no match possible */
350 
351     /* Walk the suffix of the path and the segments and
352        matching as we go
353     */
354     for(i=0;i<nsegs;i++) {
355 	CDFnode* node = (CDFnode*)nclistget(matchpath,pathstart+i);
356 	DCEsegment* seg = (DCEsegment*)nclistget(segments,i);
357 	int rank = seg->rank;
358 	segmatch = 1; /* until proven otherwise */
359 	/* Do the names match (in oc name space) */
360 	if(strcmp(seg->name,node->ocname) != 0) {
361 	    segmatch = 0;
362 	} else {
363 	    /* Do the ranks match (watch out for sequences) */
364 	    if(node->nctype == NC_Sequence)
365 		rank--; /* remove sequence pseudo-rank */
366 	    if(rank > 0
367 		&& rank != nclistlength(node->array.dimset0))
368 		segmatch = 0; /* rank mismatch */
369 	}
370 	if(!segmatch)
371 	    return 0;
372    }
373    return 1; /* all segs matched */
374 }
375 
376 /* Given the arguments to vara
377    construct a corresponding projection
378    with any pseudo dimensions removed
379 */
380 NCerror
dapbuildvaraprojection(CDFnode * var,const size_t * startp,const size_t * countp,const ptrdiff_t * stridep,DCEprojection ** projectionp)381 dapbuildvaraprojection(CDFnode* var,
382 		     const size_t* startp, const size_t* countp, const ptrdiff_t* stridep,
383 		     DCEprojection** projectionp)
384 {
385     int i,j;
386     NCerror ncstat = NC_NOERR;
387     DCEprojection* projection = NULL;
388     NClist* path = nclistnew();
389     NClist* segments = NULL;
390     int dimindex;
391 
392     /* Build a skeleton projection that has 1 segment for
393        every cdfnode from root to the variable of interest.
394        Each segment has the slices from its corresponding node
395        in the path, including pseudo-dims
396     */
397     ncstat = dapvar2projection(var,&projection);
398 
399 #ifdef DEBUG
400 fprintf(stderr,"buildvaraprojection: skeleton: %s\n",dumpprojection(projection));
401 #endif
402 
403     /* Now, modify the projection to reflect the corresponding
404        start/count/stride from the nc_get_vara arguments.
405     */
406     segments = projection->var->segments;
407     dimindex = 0;
408     for(i=0;i<nclistlength(segments);i++) {
409 	DCEsegment* segment = (DCEsegment*)nclistget(segments,i);
410         for(j=0;j<segment->rank;j++) {
411 	    size_t count = 0;
412 	    DCEslice* slice = &segment->slices[j];
413 	    /* make each slice represent the corresponding
414                start/count/stride */
415 	    slice->first = startp[dimindex+j];
416 	    slice->stride = stridep[dimindex+j];
417 	    count = countp[dimindex+j];
418 	    slice->count = count;
419 	    slice->length = count * slice->stride;
420 	    slice->last = (slice->first + slice->length) - 1;
421 	    if(slice->last >= slice->declsize) {
422 		slice->last = slice->declsize - 1;
423 		/* reverse compute the new length */
424 		slice->length = (slice->last - slice->first) + 1;
425 	    }
426 	}
427 	dimindex += segment->rank;
428     }
429 #ifdef DEBUG
430 fprintf(stderr,"buildvaraprojection.final: %s\n",dumpprojection(projection));
431 #endif
432 
433 #ifdef DEBUG
434 fprintf(stderr,"buildvaraprojection3: final: projection=%s\n",
435         dumpprojection(projection));
436 #endif
437 
438     if(projectionp) *projectionp = projection;
439 
440     nclistfree(path);
441     if(ncstat) dcefree((DCEnode*)projection);
442     return ncstat;
443 }
444 
445 int
dapiswholeslice(DCEslice * slice,CDFnode * dim)446 dapiswholeslice(DCEslice* slice, CDFnode* dim)
447 {
448     if(slice->first != 0 || slice->stride != 1) return 0;
449     if(dim != NULL) {
450 	if(slice->length != dim->dim.declsize) return 0;
451     } else if(dim == NULL) {
452 	size_t count = slice->count;
453 	if(slice->declsize == 0
454 	   || count != slice->declsize) return 0;
455     }
456     return 1;
457 }
458 
459 int
dapiswholesegment(DCEsegment * seg)460 dapiswholesegment(DCEsegment* seg)
461 {
462     int i,whole;
463     NClist* dimset = NULL;
464     unsigned int rank;
465 
466     if(seg->rank == 0) return 1;
467     if(!seg->slicesdefined) return 0;
468     if(seg->annotation == NULL) return 0;
469     dimset = ((CDFnode*)seg->annotation)->array.dimset0;
470     rank = nclistlength(dimset);
471     whole = 1; /* assume so */
472     for(i=0;i<rank;i++) {
473 	CDFnode* dim = (CDFnode*)nclistget(dimset,i);
474 	if(!dapiswholeslice(&seg->slices[i],dim)) {whole = 0; break;}
475     }
476     return whole;
477 }
478 
479 int
dapiswholeprojection(DCEprojection * proj)480 dapiswholeprojection(DCEprojection* proj)
481 {
482     int i,whole;
483 
484     ASSERT((proj->discrim == CES_VAR));
485 
486     whole = 1; /* assume so */
487     for(i=0;i<nclistlength(proj->var->segments);i++) {
488         DCEsegment* segment = (DCEsegment*)nclistget(proj->var->segments,i);
489 	if(!dapiswholesegment(segment)) {whole = 0; break;}
490     }
491     return whole;
492 }
493 
494 int
dapiswholeconstraint(DCEconstraint * con)495 dapiswholeconstraint(DCEconstraint* con)
496 {
497     int i;
498     if(con == NULL) return 1;
499     if(con->projections != NULL) {
500 	for(i=0;i<nclistlength(con->projections);i++) {
501 	 if(!dapiswholeprojection((DCEprojection*)nclistget(con->projections,i)))
502 	    return 0;
503 	}
504     }
505     if(con->selections != NULL)
506 	return 0;
507     return 1;
508 }
509 
510 
511 /*
512 Given a set of projections, we need to produce
513 an expanded, correct, and equivalent set of projections.
514 The term "correct" means we must fix the following cases:
515 1. Multiple occurrences of the same leaf variable
516    with differing projection slices. Fix is to complain.
517 2. Occurrences of container and one or more of its fields.
518    Fix is to suppress the container.
519 The term "expanded" means
520 1. Expand all occurrences of only a container by
521    replacing it with all of its fields.
522 */
523 
524 NCerror
dapfixprojections(NClist * list)525 dapfixprojections(NClist* list)
526 {
527     int i,j,k;
528     NCerror ncstat = NC_NOERR;
529     NClist* tmp = nclistnew(); /* misc. uses */
530 
531 #ifdef DEBUG
532 fprintf(stderr,"fixprojection: list = %s\n",dumpprojections(list));
533 #endif
534 
535     if(nclistlength(list) == 0) goto done;
536 
537     /* Step 1: remove duplicates and complain about slice mismatches */
538     for(i=0;i<nclistlength(list);i++) {
539 	DCEprojection* p1 = (DCEprojection*)nclistget(list,i);
540 	if(p1 == NULL) continue;
541         if(p1->discrim != CES_VAR) continue; /* don't try to unify functions */
542         for(j=i;j<nclistlength(list);j++) {
543 	    DCEprojection* p2 = (DCEprojection*)nclistget(list,j);
544 	    if(p2 == NULL) continue;
545 	    if(p1 == p2) continue;
546 	    if(p2->discrim != CES_VAR) continue;
547 	    if(p1->var->annotation != p2->var->annotation) continue;
548 	    /* check for slice mismatches */
549 	    if(!slicematch(p1->var->segments,p2->var->segments)) {
550 		/* complain */
551 		nclog(NCLOGWARN,"Malformed projection: same variable with different slicing");
552 	    }
553 	    /* remove p32 */
554 	    nclistset(list,j,(void*)NULL);
555 	    dcefree((DCEnode*)p2);
556 	}
557     }
558 
559     /* Step 2: remove containers when a field is also present */
560     for(i=0;i<nclistlength(list);i++) {
561 	DCEprojection* p1 = (DCEprojection*)nclistget(list,i);
562 	if(p1 == NULL) continue;
563         if(p1->discrim != CES_VAR) continue; /* don't try to unify functions */
564 	if(!iscontainer((CDFnode*)p1->var->annotation))
565 	    continue;
566         for(j=i;j<nclistlength(list);j++) {
567 	    DCEprojection* p2 = (DCEprojection*)nclistget(list,j);
568 	    if(p2 == NULL) continue;
569 	    if(p2->discrim != CES_VAR) continue;
570 	    nclistclear(tmp);
571 	    collectnodepath((CDFnode*)p2->var->annotation,tmp,WITHDATASET);
572 	    for(k=0;k<nclistlength(tmp);k++) {
573 		void* candidate = (void*)nclistget(tmp,k);
574 	        if(candidate == p1->var->annotation) {
575 		    nclistset(list,i,(void*)NULL);
576 	            dcefree((DCEnode*)p1);
577 		    goto next;
578 		}
579 	    }
580 	}
581 next:   continue;
582     }
583 
584     /* Step 3: expand all containers recursively down to the leaf nodes */
585     for(;;) {
586 	nclistclear(tmp);
587         for(i=0;i<nclistlength(list);i++) {
588             DCEprojection* target = (DCEprojection*)nclistget(list,i);
589             CDFnode* leaf;
590             if(target == NULL) continue;
591             if(target->discrim != CES_VAR)
592                 continue; /* don't try to unify functions */
593             leaf = (CDFnode*)target->var->annotation;
594             ASSERT(leaf != NULL);
595             if(iscontainer(leaf)) {/* capture container */
596 		if(!nclistcontains(tmp,(void*)target))
597                     nclistpush(tmp,(void*)target);
598                 nclistset(list,i,(void*)NULL);
599             }
600         }
601 	if(nclistlength(tmp) == 0) break; /*done*/
602         /* Now explode the containers */
603         for(i=0;i<nclistlength(tmp);i++) {
604             DCEprojection* container = (DCEprojection*)nclistget(tmp,i);
605 	    CDFnode* leaf = (CDFnode*)container->var->annotation;
606             for(j=0;i<nclistlength(leaf->subnodes);j++) {
607                 CDFnode* field = (CDFnode*)nclistget(leaf->subnodes,j);
608 		/* Convert field node to a proper constraint */
609 		DCEprojection* proj = projectify(field,container);
610 		nclistpush(list,(void*)proj);
611 	    }
612             /* reclaim the container */
613 	    dcefree((DCEnode*)container);
614 	}
615     } /*for(;;)*/
616 
617     /* remove all NULL elements */
618     for(i=nclistlength(list)-1;i>=0;i--) {
619         DCEprojection* target = (DCEprojection*)nclistget(list,i);
620 	if(target == NULL)
621 	    nclistremove(list,i);
622     }
623 
624 done:
625 #ifdef DEBUG
626 fprintf(stderr,"fixprojection: exploded = %s\n",dumpprojections(list));
627 #endif
628     nclistfree(tmp);
629     return ncstat;
630 }
631 
632 static int
iscontainer(CDFnode * node)633 iscontainer(CDFnode* node)
634 {
635     return (node->nctype == NC_Dataset
636                || node->nctype == NC_Sequence
637                || node->nctype == NC_Structure
638                || node->nctype == NC_Grid);
639 }
640 
641 static DCEprojection*
projectify(CDFnode * field,DCEprojection * container)642 projectify(CDFnode* field, DCEprojection* container)
643 {
644     DCEprojection* proj  = (DCEprojection*)dcecreate(CES_PROJECT);
645     DCEvar* var  = (DCEvar*)dcecreate(CES_VAR);
646     DCEsegment* seg  = (DCEsegment*)dcecreate(CES_SEGMENT);
647     proj->discrim = CES_VAR;
648     proj->var = var;
649     var->annotation = (void*)field;
650     /* Dup the segment list */
651     var->segments = dceclonelist(container->var->segments);
652     seg->rank = 0;
653     nclistpush(var->segments,(void*)seg);
654     return proj;
655 }
656 
657 static int
slicematch(NClist * seglist1,NClist * seglist2)658 slicematch(NClist* seglist1, NClist* seglist2)
659 {
660     int i,j;
661     if((seglist1 == NULL || seglist2 == NULL) && seglist1 != seglist2)
662 	return 0;
663     if(nclistlength(seglist1) != nclistlength(seglist2))
664 	return 0;
665     for(i=0;i<nclistlength(seglist1);i++) {
666 	DCEsegment* seg1 = (DCEsegment*)nclistget(seglist1,i);
667 	DCEsegment* seg2 = (DCEsegment*)nclistget(seglist2,i);
668 	if(seg1->rank != seg2->rank)
669 	    return 0;
670 	for(j=0;j<seg1->rank;j++) {
671 	    DCEslice* slice1 = &seg1->slices[j];
672 	    DCEslice* slice2 = &seg2->slices[j];
673 	    size_t count1 = slice1->count;
674 	    size_t count2 = slice2->count;
675 	    if(slice1->first != slice2->first
676 	       || count1 != count2
677 	       || slice1->stride != slice2->stride)
678 		return 0;
679 	}
680     }
681     return 1;
682 }
683 
684 /* Convert a CDFnode var to a projection; include
685    pseudodimensions; always whole variable.
686 */
687 int
dapvar2projection(CDFnode * var,DCEprojection ** projectionp)688 dapvar2projection(CDFnode* var, DCEprojection** projectionp)
689 {
690     int i,j;
691     int ncstat = NC_NOERR;
692     NClist* path = nclistnew();
693     NClist* segments;
694     DCEprojection* projection = NULL;
695     int dimindex;
696 
697     /* Collect the nodes needed to construct the projection segments */
698     collectnodepath(var,path,!WITHDATASET);
699 
700     segments = nclistnew();
701     dimindex = 0; /* point to next subset of slices */
702     nclistsetalloc(segments,nclistlength(path));
703     for(i=0;i<nclistlength(path);i++) {
704 	DCEsegment* segment = (DCEsegment*)dcecreate(CES_SEGMENT);
705 	CDFnode* n = (CDFnode*)nclistget(path,i);
706 	int localrank;
707         NClist* dimset;
708 
709 	segment->annotation = (void*)n;
710         segment->name = nulldup(n->ocname);
711         /* We need to assign whole slices to each segment */
712 	localrank = nclistlength(n->array.dimsetplus);
713         segment->rank = localrank;
714 	dimset = n->array.dimsetplus;
715         for(j=0;j<localrank;j++) {
716 	    DCEslice* slice;
717 	    CDFnode* dim;
718 	    slice = &segment->slices[j];
719 	    dim = (CDFnode*)nclistget(dimset,j);
720 	    ASSERT(dim->dim.declsize0 > 0);
721 	    dcemakewholeslice(slice,dim->dim.declsize0);
722 	}
723 	segment->slicesdefined = 1;
724 	segment->slicesdeclized = 1;
725 	dimindex += localrank;
726 	nclistpush(segments,(void*)segment);
727     }
728 
729     projection = (DCEprojection*)dcecreate(CES_PROJECT);
730     projection->discrim = CES_VAR;
731     projection->var = (DCEvar*)dcecreate(CES_VAR);
732     projection->var->annotation = (void*)var;
733     projection->var->segments = segments;
734 
735 #ifdef DEBUG1
736 fprintf(stderr,"dapvar2projection: projection=%s\n",
737         dumpprojection(projection));
738 #endif
739 
740     nclistfree(path);
741     if(ncstat) dcefree((DCEnode*)projection);
742     else if(projectionp) *projectionp = projection;
743     return ncstat;
744 }
745 
746 /*
747 Given a set of projections and a projection
748 representing a variable (from, say vara or prefetch)
749 construct a single projection for fetching that variable
750 with the proper constraints.
751 */
752 int
daprestrictprojection(NClist * projections,DCEprojection * var,DCEprojection ** resultp)753 daprestrictprojection(NClist* projections, DCEprojection* var, DCEprojection** resultp)
754 {
755     int ncstat = NC_NOERR;
756     int i;
757     DCEprojection* result = NULL;
758 #ifdef DEBUG1
759 fprintf(stderr,"restrictprojection.before: constraints=|%s| vara=|%s|\n",
760 		dumpprojections(projections),
761 		dumpprojection(var));
762 #endif
763 
764     ASSERT(var != NULL);
765 
766     /* the projection list will contain at most 1 match for the var by construction */
767     for(result=null,i=0;i<nclistlength(projections);i++) {
768 	DCEprojection* p1 = (DCEprojection*)nclistget(projections,i);
769 	if(p1 == NULL || p1->discrim != CES_VAR) continue;
770 	if(p1->var->annotation == var->var->annotation) {
771 	    result = p1;
772 	    break;
773 	}
774     }
775     if(result == NULL) {
776 	result = (DCEprojection*)dceclone((DCEnode*)var); /* use only the var projection */
777  	goto done;
778     }
779     result = (DCEprojection*)dceclone((DCEnode*)result); /* so we can modify */
780 
781 #ifdef DEBUG1
782 fprintf(stderr,"restrictprojection.choice: base=|%s| add=|%s|\n",
783 	dumpprojection(result),dumpprojection(var));
784 #endif
785     /* We need to merge the projection from the projection list
786        with the var projection
787     */
788     ncstat = dcemergeprojections(result,var); /* result will be modified */
789 
790 done:
791     if(resultp) *resultp = result;
792 #ifdef DEBUG
793 fprintf(stderr,"restrictprojection.after=|%s|\n",
794 		dumpprojection(result));
795 #endif
796     return ncstat;
797 }
798 
799 /* Shift the slice so it runs from 0..count by step 1 */
800 static void
dapshiftslice(DCEslice * slice)801 dapshiftslice(DCEslice* slice)
802 {
803     size_t first = slice->first;
804     size_t stride = slice->stride;
805     if(first == 0 && stride == 1) return; /* no need to do anything */
806     slice->first = 0;
807     slice->stride = 1;
808     slice->length = slice->count;
809     slice->last = slice->length - 1;
810 }
811 
812 int
dapshiftprojection(DCEprojection * projection)813 dapshiftprojection(DCEprojection* projection)
814 {
815     int ncstat = NC_NOERR;
816     int i,j;
817     NClist* segments;
818 
819 #ifdef DEBUG1
820 fprintf(stderr,"dapshiftprojection.before: %s\n",dumpprojection(projection));
821 #endif
822 
823     ASSERT(projection->discrim == CES_VAR);
824     segments = projection->var->segments;
825     for(i=0;i<nclistlength(segments);i++) {
826 	DCEsegment* seg = (DCEsegment*)nclistget(segments,i);
827         for(j=0;j<seg->rank;j++) {
828 	    DCEslice* slice = seg->slices+j;
829 	    dapshiftslice(slice);
830 	}
831     }
832 
833 #ifdef DEBUG1
834 fprintf(stderr,"dapshiftprojection.after: %s\n",dumpprojection(projection));
835 #endif
836 
837     return ncstat;
838 }
839 
840 /* Compute the set of variables referenced in the projections
841    of the input constraint.
842 */
843 NCerror
dapcomputeprojectedvars(NCDAPCOMMON * dapcomm,DCEconstraint * constraint)844 dapcomputeprojectedvars(NCDAPCOMMON* dapcomm, DCEconstraint* constraint)
845 {
846     NCerror ncstat = NC_NOERR;
847     NClist* vars = NULL;
848     int i;
849 
850     vars = nclistnew();
851 
852     if(dapcomm->cdf.projectedvars != NULL)
853 	nclistfree(dapcomm->cdf.projectedvars);
854     dapcomm->cdf.projectedvars = vars;
855 
856     if(constraint == NULL || constraint->projections == NULL)
857 	goto done;
858 
859     for(i=0;i<nclistlength(constraint->projections);i++) {
860 	CDFnode* node;
861 	DCEprojection* proj = (DCEprojection*)nclistget(constraint->projections,i);
862 	if(proj->discrim == CES_FCN) continue; /* ignore these */
863 	node = (CDFnode*)proj->var->annotation;
864 	if(!nclistcontains(vars,(void*)node)) {
865 	    nclistpush(vars,(void*)node);
866 	}
867     }
868 
869 done:
870     return ncstat;
871 }
872