1/* Copyright 2009, UCAR/Unidata and OPeNDAP, Inc.
2   See the COPYRIGHT file for more information. */
3
4#include "config.h"
5#ifdef HAVE_UNISTD_H
6#include <unistd.h>
7#endif
8#ifdef HAVE_SYS_TYPES_H
9#include <sys/types.h>
10#endif
11#include "ocinternal.h"
12#include "ocdebug.h"
13#include "ocdump.h"
14
15/* Mnemonic */
16#define TOPLEVEL 1
17
18/* Forward */
19static OCdatanewocdata(OCnodepattern);
20static size_t ocxdrsize(OCtype etype,int isscalar);
21static OCerror occompile1(OCstate*, OCnode*, XXDR*, OCdata**);
22static OCerror occompilerecord(OCstate*, OCnode*, XXDR*, OCdata**);
23static OCerror occompilefields(OCstate*, OCdata*, XXDR*, int istoplevel);
24static OCerror occompileatomic(OCstate*, OCdata*, XXDR*);
25static int ocerrorstring(XXDRxdrs);
26static int istoplevel(OCnodenode);
27
28/* Sequence tag constant */
29static const char StartOfSequence = '\x5A';
30static const char EndOfSequence = '\xA5';
31
32/*
33Provide an option that makes a single pass over
34the data packet and record pointers into it
35to speed up access.
36*/
37
38/* Assume we are operating on the datadds tree */
39OCerror
40occompile(OCstatestateOCnodexroot)
41{
42    OCerror ocstat = OC_NOERR;
43    XXDRxxdrs;
44    OCtreextree;
45    OCdatadata;
46
47    OCASSERT(state != NULL);
48    OCASSERT(xroot != NULL);
49    OCASSERT(xroot->tree != NULL);
50    OCASSERT(xroot->tree->dxdclass == OCDATADDS);
51    OCASSERT(xroot->tree->data.data == NULL);
52
53    xtree = xroot->tree;
54
55    xxdrs = xtree->data.xdrs;
56    if(xxdrs == NULL) return OCTHROW(OC_EXDR);
57
58    ocstat = occompile1(state,xroot,xxdrs,&data);
59    if(ocstat == OC_NOERR)
60 xtree->data.data = data;
61
62#ifdef OCDEBUG
63{
64    OCbytesbuffer = ocbytesnew();
65    ocdumpdatatree(state,data,buffer,0);
66    fprintf(stderr,"datatree:\n%s",ocbytescontents(buffer));
67    ocbytesfree(buffer);
68}
69#endif
70    return OCTHROW(ocstat);
71}
72
73static OCerror
74occompile1(OCstatestateOCnodexnodeXXDRxxdrsOCdata** datap)
75{
76    OCerror ocstat = OC_NOERR;
77    size_t i;
78    OCdatadata = NULL;
79    size_t nelements = 0;
80    OClistrecords = NULL;
81
82    /* Allocate instance for this node */
83    data = newocdata(xnode);
84    MEMFAIL(data);
85
86    /* Capture position(s) */
87    data->xdroffset = xxdr_getpos(xxdrs);
88
89    switch (xnode->octype) {
90
91    case OC_Dataset:
92    case OC_Grid: /* Always scalars */
93 ocstat = occompilefields(state,data,xxdrs,istoplevel(xnode));
94 if(ocstat != OC_NOERR) goto fail;
95 break;
96
97    case OC_Structure:
98 if(xnode->array.rank == 0) {/* scalar */
99     ocstat = occompilefields(state,data,xxdrs,istoplevel(xnode));
100     if(ocstat != OC_NOERR) goto fail;
101 } else { /* dimensioned structure */
102     unsigned int xdrcount;
103         fset(data->datamode,OCDT_ARRAY);
104     /* Determine # of instances */
105            nelements = octotaldimsize(xnode->array.rank,xnode->array.sizes);
106            if(nelements == 0) {ocstat = OCTHROW(OC_ENODATA); goto fail;}
107            /* Validate and skip the leading count field */
108            if(!xxdr_uint(xxdrs,&xdrcount))
109         {ocstat = OC_EXDR; goto fail;}
110     if(xdrcount != nelements)
111            {ocstat=OC_EINVALCOORDS; goto fail;}
112
113     /* allocate space to capture all the element instances */
114     data->instances = (OCdata**)malloc(nelements*sizeof(OCdata*));
115     MEMGOTO(data->instances,ocstat,fail);
116     data->ninstances = 0;
117
118     /* create and fill the element instances */
119     for(i=0;i<nelements;i++) {
120 OCdatainstance = newocdata(xnode);
121 MEMGOTO(instance,ocstat,fail);
122 fset(instance->datamode,OCDT_ELEMENT);
123 data->instances[i] = instance;
124 data->ninstances++;
125 /* Capture the back link */
126 instance->container = data;
127 instance->index = i;
128           /* capture the current instance position */
129 instance->xdroffset = xxdr_getpos(xxdrs);
130         /* Now compile the fields of this instance */
131 ocstat = occompilefields(state,instance,xxdrs,!TOPLEVEL);
132 if(ocstat != OC_NOERR) {goto fail;}
133     }
134 }
135        break;
136
137    case OC_Sequence:
138 /* Since we do not know the # records beforehand,
139           use a oclist to collect the record instances.
140        */
141 fset(data->datamode,OCDT_SEQUENCE);
142 records = oclistnew();
143 for(nelements=0;;nelements++) {
144            /* pick up the sequence record begin marker*/
145            char tmp[sizeof(unsigned int)];
146            /* extract the tag byte*/
147     if(!xxdr_opaque(xxdrs,tmp,(off_t)sizeof(tmp)))
148 {ocstat = OC_EXDR; goto fail;}
149            if(tmp[0] == StartOfSequence) {
150 /* Allocate a record instance */
151 OCdatarecord = NULL;
152 ocstat = occompilerecord(state,xnode,xxdrs,&record);
153         if(ocstat != OC_NOERR || !record) goto fail;
154 /* Capture the back link */
155 record->container = data;
156 record->index = nelements;
157 oclistpush(records,(void*)record);
158 record = NULL;
159            } else if(tmp[0] == EndOfSequence) {
160                break; /* we are done with the this sequence instance*/
161            } else {
162 oclog(OCLOGERR,"missing/invalid begin/end record marker\n");
163                ocstat = OC_EINVALCOORDS;
164 goto fail;
165            }
166 }
167        OCASSERT(nelements == oclistlength(records));
168 /* extract the content */
169 data->ninstances = nelements;
170 data->instances = (OCdata**)oclistdup(records);
171 MEMGOTO(data,ocstat,fail);
172 oclistfree(records);
173 records = NULL;
174        break;
175
176    case OC_Atomic:
177 fset(data->datamode,OCDT_ATOMIC);
178 ocstat = occompileatomic(state,data,xxdrs);
179 if(ocstat != OC_NOERR) goto fail;
180 break;
181
182    default:
183 OCPANIC1("occompile: encountered unexpected node type: %x",xnode->octype);
184        break;
185    }
186
187/*ok:*/
188    if(datap) {
189 *datap = data;
190 data = NULL;
191    }
192
193    if(data != NULL)
194 ocdata_free(state,data);
195
196    return OCTHROW(ocstat);
197
198fail:
199    /* See if we can extract error info from the response */
200    ocerrorstring(xxdrs);
201
202    if(records != NULL) {
203 for(i=0;i<oclistlength(records);i++)
204     ocdata_free(state,(OCdata*)oclistget(records,i));
205 oclistfree(records);
206    }
207
208    if(data != NULL)
209 ocdata_free(state,data);
210
211    return OCTHROW(ocstat);
212}
213
214static OCerror
215occompilerecord(OCstatestateOCnodexnodeXXDRxxdrsOCdata** recordp)
216{
217    OCerror ocstat = OC_NOERR;
218    OCdatarecord = newocdata(xnode);/* create record record */
219    MEMFAIL(record);
220    fset(record->datamode,OCDT_RECORD);
221    record->pattern = xnode;
222    /* capture the current record position */
223    record->xdroffset = xxdr_getpos(xxdrs);
224    /* Compile the fields of this record */
225    ocstat = OCTHROW(occompilefields(state,record,xxdrs,!TOPLEVEL));
226    if(ocstat == OC_NOERR) {
227        if(recordp) {
228     *recordp = record;
229     record = NULL;
230 }
231        if(record != NULL)
232     ocdata_free(state,record);
233    }
234    return OCTHROW(ocstat);
235}
236
237static OCerror
238occompilefields(OCstatestateOCdatadataXXDRxxdrs, int istoplevel)
239{
240    size_t i;
241    OCerror ocstat = OC_NOERR;
242    size_t nelements;
243    OCnodexnode = data->pattern;
244
245    assert(data != NULL);
246    nelements = oclistlength(xnode->subnodes);
247    if(nelements == 0)
248 goto done;
249    data->instances = (OCdata**)malloc(nelements*sizeof(OCdata*));
250    MEMFAIL(data->instances);
251    for(i=0;i<nelements;i++) {
252        OCnodefieldnode;
253        OCdatafieldinstance;
254 fieldnode = (OCnode*)oclistget(xnode->subnodes,i);
255        ocstat = occompile1(state,fieldnode,xxdrs,&fieldinstance);
256 if(ocstat != OC_NOERR)
257     goto fail;
258 fset(fieldinstance->datamode,OCDT_FIELD);
259 data->instances[i] = fieldinstance;
260 data->ninstances++;
261 /* Capture the back link */
262 fieldinstance->container = data;
263        fieldinstance->index = i;
264    }
265
266    /* If top-level, then link the OCnode to the OCdata directly */
267    if(istoplevel) {
268 for(i=0;i<nelements;i++) {
269            OCnodefieldnode = (OCnode*)oclistget(xnode->subnodes,i);
270            OCdatafieldinstance = data->instances[i];
271     fieldnode->data = fieldinstance;
272 }
273    }
274
275done:
276    return OCTHROW(ocstat);
277
278fail:
279    if(data->instances != NULL) {
280 for(i=0;i<data->ninstances;i++)
281     ocdata_free(state,data->instances[i]);
282 data->ninstances = 0;
283    }
284    return OCTHROW(ocstat);
285}
286
287static OCerror
288occompileatomic(OCstatestateOCdatadataXXDRxxdrs)
289{
290    OCerror ocstat = OC_NOERR;
291    int i;
292    off_t nelements,xdrsize;
293    unsigned int xxdrcount;
294    OCnodexnode = data->pattern;
295    int scalar = (xnode->array.rank == 0);
296
297    OCASSERT((xnode->octype == OC_Atomic));
298
299    if(!scalar) {
300        /* Use the count from the datadds */
301        nelements = octotaldimsize(xnode->array.rank,xnode->array.sizes);
302        /* Get first copy of the dimension count */
303        if(!xxdr_uint(xxdrs,&xxdrcount)) {ocstat = OC_EXDR; goto fail;}
304        if(xxdrcount != nelements) {ocstat=OC_EINVALCOORDS; goto fail;}
305        if(xnode->etype != OC_String && xnode->etype != OC_URL) {
306            /* Get second copy of the dimension count */
307            if(!xxdr_uint(xxdrs,&xxdrcount)) {ocstat = OC_EXDR; goto fail;}
308            if(xxdrcount != nelements) {ocstat=OC_EINVALCOORDS; goto fail;}
309        }
310    } else { /*scalar*/
311 nelements = 1;
312 xxdrcount = 1;
313    }
314
315    data->xdroffset = xxdr_getpos(xxdrs);
316    data->ninstances = xxdrcount;
317    data->xdrsize = ocxdrsize(xnode->etype,scalar);
318
319    switch (xnode->etype) {
320
321    /* Do the fixed sized, non-packed cases */
322    case OC_Int16: case OC_UInt16:
323    case OC_Int32: case OC_UInt32:
324    case OC_Int64: case OC_UInt64:
325    case OC_Float32: case OC_Float64:
326 /* Skip the data */
327 xxdr_skip(xxdrs,data->ninstances*data->xdrsize);
328 break;
329
330    /* Do the fixed sized, possibly packed cases */
331    case OC_Byte:
332    case OC_UByte:
333    case OC_Char:
334 /* Get the totalsize and round up to multiple of XDRUNIT */
335 xdrsize = data->ninstances*data->xdrsize;
336 xdrsize = RNDUP(xdrsize);
337 /* Skip the data */
338 xxdr_skip(xxdrs,xdrsize);
339 break;
340
341    /* Hard case, because strings are variable length */
342    case OC_String: case OC_URL:
343 /* Start by allocating a set of pointers for each string */
344 data->nstrings = xxdrcount;
345 data->strings = (off_t*)malloc(sizeof(off_t)*data->nstrings);
346 /* We need to walk each string, get size, then skip */
347        for(i=0;i<data->nstrings;i++) {
348     unsigned int len;
349     off_t lenz;
350     data->strings[i] = xxdr_getpos(xxdrs);
351     /* get exact string length */
352     if(!xxdr_uint(xxdrs,&len)) {ocstat = OC_EXDR; goto fail;}
353     lenz = (off_t)len;
354     lenz = RNDUP(lenz);
355     /* Skip the data */
356     xxdr_skip(xxdrs,lenz);
357        }
358        break;
359
360    default:
361 OCPANIC1("unexpected etype: %d",xnode->etype);
362
363    } /* switch */
364
365/*ok:*/
366    return OCTHROW(ocstat);
367
368fail:
369    if(data->strings != NULL)
370 free(data->strings);
371    data->strings = NULL;
372    data->ninstances = 0;
373    return OCTHROW(ocstat);
374}
375
376void
377ocdata_free(OCstatestateOCdatadata)
378{
379    if(data == NULL)
380 return;
381
382    if(data->instances != NULL) {
383 int i;
384        for(i=0;i<data->ninstances;i++)
385     ocdata_free(state,data->instances[i]);
386 free(data->instances);
387    }
388    if(data->strings != NULL)
389 free(data->strings);
390    free(data);
391}
392
393static OCdata*
394newocdata(OCnodepattern)
395{
396    OCdatadata = (OCdata*)calloc(1,sizeof(OCdata));
397    MEMCHECK(data,NULL);
398    data->header.magic = OCMAGIC;
399    data->header.occlass = OC_Data;
400    data->pattern = pattern;
401    return data;
402}
403
404static int
405istoplevel(OCnodenode)
406{
407    if(node == NULL)
408 return 1; /* base case */
409    if(!istoplevel(node->container))
410 return 0;
411    switch (node->octype) {
412    case OC_Dataset: case OC_Grid: case OC_Atomic: return 1;
413    case OC_Structure:
414 return (node->array.rank == 0 ? 1 : 0); /* Toplevel if scalar */
415    case OC_Sequence: default: return 0;
416    }
417    return 1;
418}
419
420
421/* XDR representation size depends on if this is scalar or not */
422static size_t
423ocxdrsize(OCtype etype, int isscalar)
424{
425    switch (etype) {
426    case OC_Char:
427    case OC_Byte:
428    case OC_UByte:
429 return (isscalarXDRUNIT : 1);
430    case OC_Int16:
431    case OC_UInt16:
432    case OC_Int32:
433    case OC_UInt32:
434    case OC_Float32:
435 return XDRUNIT;
436    case OC_Float64:
437    case OC_Int64:
438    case OC_UInt64:
439 return 2*XDRUNIT;
440    default:
441 break; /* no simple size */
442    }
443    return 0;
444}
445
446#define tag "Error {\n"
447
448static int
449ocerrorstring(XXDRxdrs)
450{
451    /* Check to see if the xdrs contains "Error {\n'; assume it is at the beginning of data */
452    off_t avail = xxdr_getavail(xdrs);
453    char* data;
454    if(!xxdr_setpos(xdrs,(off_t)0)) return 0;
455    data = (char*)malloc((size_t)avail);
456    MEMCHECK(data,0);
457    if(!xxdr_opaque(xdrs,data,avail)) {free(data); return 0;}
458    /* check for error tag at front */
459    if(ocstrncmp(data,tag,sizeof(tag))==0) {
460 char* p;
461        if((p=strchr(data,'}')) != NULL) *(++p)='\0';
462        oclog(OCLOGERR,"Server error: %s",data);
463        /* Since important, report to stderr as well */
464        fprintf(stderr,"Server error: %s",data);
465 return 1;
466    }
467    return 0;
468}


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