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