Version 2.0 introduces a new access model in an attempt to provide a cleaner API.
This document is intended for end users and provides a short tutorial on using the API. The API itself is documented in the file ocuserman.html. Familiarity with DAP2 concepts such as DAS, DDS, and DataDDS are assumed.
The oc API is based on the concept of tree data structures. The DDS, the DAS, the DATADDS, and the DATADDS data (aka data tree) all are represented as trees. Trees are recursive in that a given node has "subnodes" as defined by the node's edges. So, given a tree, it is possible to "traverse" it starting at its root and moving to sucessive subnodes.
Consider, for example, this DDS.
Dataset { Structure { int32 f0; float64 f1; } S1[2]; byte v1; } D;We can represent the corresponding tree as follows, with depth levels at the left.
[01] Dataset D [02] Structure S1[2] [03] int32 f0 [04] float32 f1 [05] byte v1;Alternatively, in graph form, it looks like this.
The Dataset, D, is the top level node. D has two subnodes: S1 and v1. S1, in turn, has two subnodes: f0, and f1. These nodes – f0, f1, and v1 – are sometimes referred to as leaves because they have no subnodes and cannot be further traversed.
The key operations of the API consist of obtaining a reference to the root node of a tree and then "walking" around the tree by asking for the subnodes of a node, or asking for the container of a node.
Be warned, however, that walking around a data tree has some complications not present when walking around a DDS tree.
In addition to tree movement operations, there are operations to
Every time that the client invokes the oc_fetch procedure the following actions occur.
The client code can perform as many fetches as it desires with varying constraints on the fetch request. The root nodes of the resulting trees are available for client actions. When finished with a tree, the user can (and should) release the tree and have its resources reclaimed.
It should also be noted that as a rule, the DAS tree is rarely used directly. Rather, an operation called oc_merge_das is provided to merge the attributes in the DAS into some DDS tree for later access.
The primitive types are as follows.
It should be noted that the type enumeration in oc.h actually defines additional primitive types that are currently unused. These are OC_Ubyte, OC_Char, OC_Int64, and OC_UInt64.
The container types are as follows.
/*Example 1*/ 01 int 02 main(int argc, char **argv) 03 { 04 char* fileurl = NULL; 05 OClink link; 06 OCddsnode ddsroot, dasroot; 07 08 if(argv[1] == NULL) { 09 fprintf(stderr,"no file url specified\n"); 10 exit(1); 11 } 12 13 fileurl = strdup(argv[1]); 14 check_err(oc_open(fileurl,&link)); 15 check_err(oc_fetch(link,NULL,OCDAS,0,&dasroot)); 16 check_err(oc_fetch(link,NULL,OCDDS,0,&ddsroot)); 17 check_err(oc_merge_das(link,dasroot,ddsroot)); 18 19 /* Walk and output the DDS */ 20 generatedds(link, ddsroot, 0); 21 exit(0); 22 } 23Lines 4-6 declare some variables for use in subsequent code.
Line 14 opens a connection to the DAP server as specified by the URL that is the command line argument. The instance, of type OClink is stored in the variable named link.
Line 15 uses the link to fetch the DAS for the data source specified by the URL. The DAS is fetched, parsed, and its root node is returned in the dasroot variable (of type OCddsnode).
Line 16 uses the link to fetch the DDS for the data source specified by the URL. The DDS is fetched, parsed, and its root node is returned in the ddsroot variable (of type OCddsnode).
Line 17 "merges" the DAS into the DDS. It annotates the DDS nodes with attributes taken from the DAS. The merge algorithm is somewhat convoluted to match the original lib-nc library. It will not be explained in this document.
Line 20 initiates traversal by calling a recursive procedure to traverse the tree and print out the nodes of the tree. The last argument, the "0" indicates the depth we are at in the tree. This will be used to control indenting.
For all the "oc_XXX" calls, an error code is returned and checked by the check_err function. If it is an error rather than OC_NOERR, then the program will fail at that point.
01 static void 02 generatedds(OClink link, OCddsnode node, int depth) 03 { 04 size_t i,rank,nattr,nsubnodes; 05 OCtype octype, atomtype; 06 OCddsnode container; 07 OCddsnode* dimids; 08 size_t size; 09 char tmp[1024]; 10 char id1[1024]; 11 char* name; 12 13 indent(depth); 14 /* get all info about the node */ 15 check_err(oc_dds_properties(link,node,&name,&octype,&atomtype,&container, 16 &rank,&nsubnodes,&nattr)); 17Line 14 establishes the indent level based on the tree depth. This is purely to produce a nicer looking output.
Line 17 starts by obtaining all known information about that current node. The information is obtained by passing in pointers to variables into which to store the node information. Specifically, this includes the following.
01 if(octype == OC_Atomic) { 02 OCddsnode dimids[OC_MAX_DIMENSIONS]; 03 printf("%s %s", oc_typetostring(atomtype),name); 04 /* dump dim info*/ 05 if(rank > 0) { 06 check_err(oc_dds_dimensions(link,node,dimids)); 07 for(i=0;i<rank;i++) { 08 OCddsnode dim = dimids[i]; 09 char* dimname = NULL; 10 check_err(oc_dimension_properties(link,dim,&size,&dimname)); 11 if(dimname == NULL) 12 printf("[%lu]",(unsigned long)size); 13 else 14 printf("[%s=%lu]",dimname,(unsigned long)size); 15 if(dimname) free(dimname); 16 } 17 } 18 printf(";\n"); 19 generateddsattributes(link,node,depth+1);If the instance is referencing a leaf node (line 1), then we print out a line of this form (properly indented).
{type name} {field name} {dimensions} ;The type name and field name are printed in line 3.
In order to print the dimensions, we need to get references to the dimension nodes associated with the field. Space for the dimension nodes is pre-allocated in line 2. Alternately one could have invoked malloc to dynamically allocate the space. The dimension nodes are obtained in line 5.
In lines 6-15, we iterate over each dimension node, obtain its name and size (line 9), and print it out. Since a dimension may be anonymous (i.e. has no name), we must be prepared to print the dimension with a name (line 13) or without (line 11).
As the last order of business (line 17), we print out any attributes associated with the leaf node. This procedure will be discussed later.
01 } else { /* Must be a container */ 02 OCddsnode field; 03 switch (octype) { 04 case OC_Dataset: 05 case OC_Structure: 06 case OC_Sequence: 07 printf("%s {\n",oc_typetostring(octype)); 08 for(i=0;i<nsubnodes;i++) { 09 /* Get the i'th field (i.e. subnode) of this container */ 10 check_err(oc_dds_ithfield(link,node,i,&field)); 11 generatedds(link,field,depth+1); 12 } 13 indent(depth); printf("} %s",name); 14 /* print structure dimensions, if any */ 15 if(rank > 0 && octype == OC_Structure) { 16 OCddsnode dimids[OC_MAX_DIMENSIONS]; 17 check_err(oc_dds_dimensions(link,node,dimids)); 18 for(i=0;i<rank;i++) { 19 char* dimname = NULL; 20 OCddsnode dim = dimids[i]; 21 check_err(oc_dimension_properties(link,dim,&size,&dimname)); 22 printf("["); 23 if(dimname != NULL) printf("%s=",dimname); 24 printf("%lu]",(unsigned long)size); 25 if(dimname) free(dimname); 26 } 27 } 28 printf("\n",name); 29 break; 30 case OC_Grid: 31 printf("%s {\n",oc_typetostring(octype)); 32 /* Get the Array field (i.e. subnode zer0) of this Grid */ 33 indent(depth); printf("Array:\n"); 34 check_err(oc_dds_ithfield(link,node,0,&field)); 35 generatedds(link,field,depth+2); 36 /* Print the map fields */ 37 indent(depth); printf("Maps:\n"); 38 for(i=1;i<nsubnodes;i++) { 39 check_err(oc_dds_ithfield(link,node,1,&field)); 40 generatedds(link,field,depth+2); 41 } 42 indent(depth); printf("} %s\n",name); 43 break; 44 } 45 } 46 generateddsattributes(link,node,depth+1); 47 oc_dds_free(link,node); 48 if(name) free(name); 49 } 50This is where this procedure goes recursive. The idea is to print out the header of this container (e.g. "Structure {") (line 7) with proper indentation and then recursively print out the fields of the structure.
The fields of the container must be obtained one by one by obtaining the node for each field in turn (line 10). Then this procedure is called recursively (line 11) to print out the field.
After the loop over the fields, it is time to print out the container's name (line 13) with proper indentation.
If the container was a Structure, then it may have dimensions, so they must be printed out (lines 15-27). The code is similar to that shown previously.
In order to print a Grid, we must distinguish the Array variable (field 0) from the Map variables (fields 1 to nsubnodes). This is done in lines 30-43.
Again (line 46), any attributes associated with the container must be printed out.
Finally (line 47), we must free the previously allocated name and (line 48) tell the oc library we no longer will use this node.
01 static void 02 generateddsattributes(OClink link, OCddsnode node, int depth) 03 { 04 size_t i,j; 05 size_t nattrs; 06 char tmp[128]; 07 char* aname; 08 char* name; 09 OCtype atomictype; 10 size_t nvalues; 11 char id1[1024]; 12 13 check_err(oc_dds_attr_count(link,node,&nattrs)); 14 check_err(oc_dds_name(link,node,&name)); 15 check_err(oc_dds_name(link,node,&name)); 16 if(nattrs > 0) { 17 for(i=0;i<nattrs;i++) { 18 char** values; 19 check_err(oc_dds_attr(link,node,i,NULL,NULL,&nvalues,NULL)); 20 values = (char**)malloc(sizeof(char*)*nvalues); 21 if(values == NULL) check_err(OC_ENOMEM); 22 check_err(oc_dds_attr(link,node,i,&aname,&atomictype, 23 &nvalues,values)); 24 indent(depth); printf("%s %s:%s = ", 25 oc_typetostring(atomictype),name,aname); 26 for(j=0;j<nvalues;j++) { 27 if(j > 0) printf(", "); 28 printf("%s",values[j]); 29 } 30 printf(";\n"); 31 free(aname); 32 oc_reclaim_strings(nvalues,values); 33 free(values); 34 } 35 } 36 free(name); 37 } 38This procedure is given the node whose attributes are to be printed as an argument.
The processing steps are as follows.
01 static void 02 check_err(int stat) 03 { 04 if(stat == OC_NOERR) return; 05 fprintf(stderr,"error status returned: (%d) %s\n", 06 stat,ocerrstring(stat)); 07 fflush(stdout); fflush(stderr); 08 exit(1); 09 } 10 11 static void 12 indent(int depth) 13 { 14 while(depth-- > 0) printf(" "); 15 }
This is an optimization in the sense that the code will work correctly if unused OCddsnode references are not freed. However, this may have impacts on memory efficiency and so it is a desirable action.
As mentioned in the introduction, walking the data tree is more complex than walking a DDS. Consider this DDS.
Dataset { Structure { Structure { int32 f1[2]; } S2[3]; } S1[2]; } D;Note the following.
A nested box graph shows the situation a little more clearly.
This is the key fact: each node in the DDS tree may have multiple instances in the data tree. This makes traversal more complex.
Note also that this is the situation with Sequences as well. A Sequence instance may have an arbitrary number of associated records, where a record is an instance of that Sequence.
Note also that every instance maps uniquely to a node in the DDS. In effect, the DDS nodes serve as "templates" for the instances in the data tree.
As with a DDS tree, we start by getting a reference to the root of the data tree. of the data tree. The term "instance" (of type OCdatanode) is used to remind the user that a data tree is being walked.
Given an instrance, we can do the following things.
In order to walk the data tree, we define three functions: FC, FF, and FL. FC walks the instances of a container and FF walks the fields of a container instances. They are mutually recursive. FL walks the leaves and invokes "process" on each element of the leaf.
This program assumes the following.
/*Example 2*/ 01 static OCerror 02 FC(OClink link, OCdatanode instance) 03 { 04 int i; 05 OCddsnode node; 06 size_t rank; 07 OCtype octype; 08 size_t nsubnodes; 09 10 /* Get the OCddsnode object associated with the current instance */ 11 FAIL(oc_data_ddsnode(link,instance,&node)); 12 /* Obtain some information about the node */ 13 FAIL(oc_dds_octype(link,node,&octype)); 14 FAIL(oc_dds_rank(link,node,&rank)); 15 FAIL(oc_dds_nsubnodes(link,node,&nsubnodes)); 16The processing proceeds as follows.
01 if(octype == OC_Structure && rank > 0) { 02 size_t dimsizes[OC_MAX_DIMENSIONS]; 03 size_t indices[OC_MAX_DIMENSIONS]; /* records current indices 04 using the odometer code */ 05 /* walk all the elements of the dimensioned Structure */ 06 /* Obtain the dimension sizes of the Structure (via the template node) */ 07 FAIL(oc_dds_dimensionsizes(link,node,dimsizes)); 08 /* Initialize the odometer indices to zero */ 09 for(i=0;i<rank;i++) indices[i] = 0; 10 /* Iterate over all elements of the array of Structures */ 11 while(odom_more(rank,dimsizes,indices)) { 12 OCdatanode arrayelement; 13 /* Get the instance of the current array element */ 14 FAIL(oc_data_ithelement(link,instance,indices,&arrayelement)); 15 FAIL(FC(link,arrayelement)); /* recurse to visit this structure array instance */ 16 /* No longer need this array element instance */ 17 FAIL(oc_data_free(link,arrayelement)); 18 odom_next(rank,dimsizes,indices); 19 }The processing proceeds as follows.
01 } else if(octype == OC_Sequence) { 02 /* walk each record of the sequence. We have two choices, 03 one is to pre-compute the number of records using 04 oc_data_recordcount and the other is to just iterate 05 until we get OC_EINDEX error return. We use this latter approach. 06 */ 07 for(i=0;;i++) { 08 OCdatanode recordinstance; 09 OCerror stat = oc_data_ithrecord(link,instance,i,&recordinstance); 10 if(stat != OC_NOERR) { 11 if(stat == OC_EINDEX) 12 break; /* done */ 13 else FAIL(stat); 14 } 15 FF(link,recordinstance); /* Recurse */ 16 FAIL(oc_data_free(link,recordinstance)); 17 } 18 } else { /* Singleton Container: Grid or Dataset */ 19 FAIL(FF(link,instance)); /* Recurse */ 20 } 21 return OC_NOERR; 22 } 23The processing proceeds as follows.
The last case to consider (line 19) is that we have a container that only has a single instance (all other cases). We then need to just invoke FF on that instance.
01 static OCerror 02 FF(OClink link, OCdatanode instance) 03 { 04 int i; 05 OCddsnode node; 06 size_t rank; 07 OCtype octype; 08 size_t nsubnodes; 09 10 /* Get the OCddsnode object associated with the current instance */ 11 FAIL(oc_data_ddsnode(link,instance,&node)); 12 /* Obtain some information about the node */ 13 FAIL(oc_dds_octype(link,node,&octype)); 14 FAIL(oc_dds_rank(link,node,&rank)); 15 FAIL(oc_dds_nsubnodes(link,node,&nsubnodes)); 16The initial code for FF is similar to that of FC; it gets the instance's template node and some info about the template node.
FF needs to walk the field instances of the instance argument.
01 if(octype == OC_Atomic) { 02 return FL(link,instance); 03 } else {/* Instance is a container */ 04 /* For all these cases, we want to visit the fields in turn. */ 05 for(i=0;i<nsubnodes;i++) { 06 OCdatanode fieldinstance; 07 /* get a new instance node for the i'th field */ 08 FAIL(oc_data_ithfield(link,instance,i,&fieldinstance)); 09 FAIL(FC(link,fieldinstance)); /* recurse */ 10 FAIL(oc_data_free(link,fieldinstance)); 11 } 12 } 13 return OC_NOERR; 14 } 15Processing of a container instance proceeds as follows.
01 static OCerror 02 FL(OClink link, OCdatanode instance) 03 { 04 int i; 05 OCddsnode node; 06 size_t rank; 07 OCtype octype,atomtype; 08 size_t elemsize; 09 size_t memsize; 10 char* memory; 11 size_t count; 12 size_t dimsizes[OC_MAX_DIMENSIONS]; 13 size_t indices[OC_MAX_DIMENSIONS]; /* records current indices 14 using the odometer code */ 15 16 /* Get the OCddsnode object associated with the current instance */ 17 FAIL(oc_data_ddsnode(link,instance,&node)); 18 /* Obtain some information about the node */ 19 FAIL(oc_dds_octype(link,node,&octype)); 20 FAIL(oc_dds_atomictype(link,node,&atomtype)); 21 FAIL(oc_dds_rank(link,node,&rank)); 22 elemsize = oc_typesize(atomtype); /* get memory size of each primitive value */ 23The initial code for FL is similar to that of FC; it gets the instance's template node and some info about the template node. At line 24, we obtain the in-memory size of the atomic type: sizeof(int) for int32, sizeof(char*) for string, etc.
01 if(rank == 0) {/* Scalar case */ 02 memory = calloc(elemsize,1); /* reading only one value */ 03 /* read the scalar */ 04 FAIL(oc_data_read(link,instance,NULL,NULL,elemsize,memory)); 05 count = 1;In the scalar case (line 1), we allocate memory for a single instance of the atomic type. We then invoke the oc_data_read procedure to actually pull out the data from the instance into memory. The two NULLs are used because reading a scalar ignores those arguments.
01 } else {/* rank > 0 */ 02 size_t offset; 03 /* Initialize the odometer indices to zero */ 04 odom_init(rank,indices); 05 /* Obtain the dimension sizes */ 06 FAIL(oc_dds_dimensionsizes(link,node,dimsizes)); 07 /* Compute the cross product */ 08 for(count=1,i=0;i<rank;i++) count *= dimsizes[i]; 09 /* Use the cross product to allocate enough memory */ 10 memsize = elemsize*count; 11 memory = calloc(memsize,1); /* allocate the memory */ 12 if(memory == NULL) 13 return OC_ENOMEM; 14 /* This code also has a conditional to show extracting 15 the data piece by piece versus all at once 16 */ 17 { 18 #ifdef ALLATONCE 19 /* Read whole leaf at one time */ 20 /* the indices variable should be all zeros at this point */ 21 FAIL(oc_data_read(link,instance,indices,dimsizes,memsize,memory)); 22 #else 23 size_t offset; 24 size_t one[OC_MAX_DIMENSIONS]; 25 /* Initialize the read-by-one counts */ 26 for(i=0;i<rank;i++) one[i]=1; 27 /* Read the data item by item using odometer approach */ 28 for(offset=0;odom_more(rank,dimsizes,indices);offset+=elemsize) { 29 FAIL(oc_data_read(link,instance,indices,one,elemsize,memory+offset)); 30 odom_next(rank,dimsizes,indices); 31 } 32 #endif 33 } 34 } 35 process(atomtype,rank,count,memory,elemsize); 36 if(atomtype == OC_String || atomtype == OC_URL) 37 oc_reclaim_strings(count,(char**)memory); 38 free(memory); 39 return OC_NOERR; 40 } 41This code has two alternatives: (1) we read item by item using an odometer, or (2) we read all the elements at once.
In either case, we will need a starting point of a set of indices all with the value zero (line 4). We also need to get the dimension sizes (line 7) and we compute the cross-product of the dimension sizes (line 9). The cross-product tells us the total number of elements in the array.
We use the total number of elements to allocate enough memory (lines 12-15).
Now, assuming we want to read the whole atomic leaf instance at once, we invoke oc_data_read procedure (line 22) to actually pull out the data from the instance into memory. We tell it the starting set of indices (all zeros), the dimension sizes, the memory in which to store the data, and the amount of memory allocated.
Instead of reading the leaf instance all-at-once, we can read one-by-one by using an odometer to iterate over the elements of the atomic leaf array. The processing goes as follows in this case.
However the contents of the leaf is read, the "process" function is invoked to process the data in some format (line 37);
Finally (lines 38-40), we reclaim allocated memory.
01 static void 02 process(OCtype atomtype, size_t rank, size_t nelements, char* memory, size_t elemsize) 03 { 04 int i; 05 char* p; 06 07 printf("%s",oc_typetostring(atomtype)); 08 if(rank > 0) 09 printf("[%d]",(int)nelements); 10 printf("=",(int)nelements); 11 12 for(p=memory,i=0;i0) printf(","); 14 switch (atomtype) { 15 case OC_Byte: 16 printf("%hhu",*p); 17 break; 18 case OC_Int16: 19 printf("%hd",*(short*)p); 20 break; 21 case OC_UInt16: 22 printf("%hu",*(unsigned short*)p); 23 break; 24 case OC_Int32: 25 printf("%d",*(int*)p); 26 break; 27 case OC_UInt32: 28 printf("%u",*(unsigned int*)p); 29 break; 30 case OC_Float32: 31 printf("%g",*(float*)p); 32 break; 33 case OC_Float64: 34 printf("%g",*(double*)p); 35 break; 36 case OC_String: case OC_URL: 37 printf("\"%s\"",*(char**)p); 38 break; 39 default: 40 break; 41 } 42 } 43 printf("\n"); 44 } 45
01 int 02 main(int argc, char **argv) 03 { 04 char* fileurl = NULL; 05 OClink link; 06 OCddsnode dataddsroot, dasroot; 07 OCdatanode rootinstance; 08 09 if(argv[1] == NULL) { 10 fprintf(stderr,"no file url specified\n"); 11 exit(1); 12 } 13 fileurl = strdup(argv[1]); 14 FAIL(oc_open(fileurl,&link)); 15 FAIL(oc_fetch(link,NULL,OCDAS,0,&dasroot)); 16 FAIL(oc_fetch(link,NULL,OCDATADDS,0,&dataddsroot)); 17 FAIL(oc_merge_das(link,dasroot,dataddsroot)); 18 /* Get the root instance */ 19 FAIL(oc_data_getroot(link,dataddsroot,&rootinstance)); 20 21 /* apply FC to the instance tree to start because we know 22 that the root is a Dataset => container */ 23 FC(link, rootinstance); 24 oc_root_free(link,dasroot); 25 oc_root_free(link,dataddsroot); /* Also frees the associated data tree */ 26 exit(0); 27 } 28
Lines 4-7 declare some OCddsnode variables for use in subsequent code.
Line 15 opens a connection to the DAP server as specified by the URL that is the command line argument. The instance, of type OClink is stored in the variable named link.
Line 16 uses the link to fetch the DAS for the data source specified by the URL. The DAS is fetched, parsed, and its root node is returned in the dasroot variable (of type OCddsnode).
Line 17 uses the link to fetch the DataDDS for the data source specified by the URL. Recall that this will fetch both the DDS part and the instance part. The DDS part is fetched, parsed, and its root node is returned in the dataddsroot variable (of type OCddsnode).
Line 18 "merges" the DAS into the DataDDS. It annotates the DataDDS nodes with attributes taken from the DAS. The merge algorithm is somewhat convoluted, so it will not be explained in this document.
Line 21 obtains an instance reference to the root of the data part of the DataDDS. The reference is store in the rootinstance variable.
Line 25 invokes the FC function. FC is called because by definition, the root instance refers to a Dataset instance, which by definition is not dimensioned.
Finally, lines 27-28 free up the original DAS and DDS trees.
For example, suppose we have the set of dimensions [2][3][2]. We need to generate the following set of dimension values.
0,0,0 0,0,1 0,1,0 0,1,1 0,2,0 0,2,1 1,0,0 1,0,1 1,1,0 1,1,1 1,2,0 1,2,1This order, where the rightmost index varies the fastest, is often called row-major order. It is that used by C/C++. Fortran uses column-major order, where the leftmost index varies the fastest. The oc library assumes row-major order.
The odometer is represented by a vector of indices (of size 3 in the example immediately above).
We need two procedures.
01 static void odom_init(size_t rank, size_t* indices) 02 { 03 int i; 04 for(i=0;i<rank;i++) indices[i] = 0; 05 } 06The odom_init procedure initializes our vector of indices all to zero.
01 static int odom_more(size_t rank, size_t* dimsizes, size_t* indices) 02 { 03 return (indices[0] < dimsizes[0] ? 1 : 0); 04 } 05The odom_more procedure returns 0 if we have covered all the possible combinations, and 1 otherwise (line 3). There is more as long as the first index (indices[0]) has not reached its maximum value.
01 static void odom_next(size_t rank, size_t* dimsizes, size_t* indices) 02 { 03 int i; 04 for(i=rank-1;i>=0;i--) { 05 indices[i]++; 06 if(indices[i] < dimsizes[i]) break; 07 if(i > 0) indices[i] = 0; 08 } 09 } 10The odom_next procedure moves to the next index element. The next set of indices is established by incrementing each index by one starting at the leftmost dimension. If at any point, the i'th index has not overflowed (i.e. reached its maximum), then we can stop. Otherwise, reset the i'th index to 0 and move to the left. In order for odom_more to work, indices[0] is not reset when it overflows.
int main(int argc, char **argv) { char* fileurl = NULL; OClink link; OCddsnode ddsroot, dasroot; if(argv[1] == NULL) { fprintf(stderr,"no file url specified\n"); exit(1); } fileurl = strdup(argv[1]); check_err(oc_open(fileurl,&link)); check_err(oc_fetch(link,NULL,OCDAS,0,&dasroot)); check_err(oc_fetch(link,NULL,OCDDS,0,&ddsroot)); check_err(oc_merge_das(link,dasroot,ddsroot)); /* Walk and output the DDS */ generatedds(link, ddsroot, 0); exit(0); } static void generatedds(OClink link, OCddsnode node, int depth) { size_t i,rank,nattr,nsubnodes; OCtype octype, atomtype; OCddsnode container; OCddsnode* dimids; size_t size; char tmp[1024]; char id1[1024]; char* name; indent(depth); /* get all info about the node */ check_err(oc_dds_properties(link,node,&name,&octype,&atomtype,&container, &rank,&nsubnodes,&nattr)); if(octype == OC_Atomic) { OCddsnode dimids[OC_MAX_DIMENSIONS]; printf("%s %s", oc_typetostring(atomtype),name); /* dump dim info*/ if(rank > 0) { check_err(oc_dds_dimensions(link,node,dimids)); for(i=0;i<rank;i++) { OCddsnode dim = dimids[i]; char* dimname = NULL; check_err(oc_dimension_properties(link,dim,&size,&dimname)); if(dimname == NULL) printf("[%lu]",(unsigned long)size); else printf("[%s=%lu]",dimname,(unsigned long)size); if(dimname) free(dimname); } } printf(";\n"); generateddsattributes(link,node,depth+1); } else { /* Must be a container */ OCddsnode field; switch (octype) { case OC_Dataset: case OC_Structure: case OC_Sequence: printf("%s {\n",oc_typetostring(octype)); for(i=0;i<nsubnodes;i++) { /* Get the i'th field (i.e. subnode) of this container */ check_err(oc_dds_ithfield(link,node,i,&field)); generatedds(link,field,depth+1); } indent(depth); printf("} %s",name); /* print structure dimensions, if any */ if(rank > 0 && octype == OC_Structure) { OCddsnode dimids[OC_MAX_DIMENSIONS]; check_err(oc_dds_dimensions(link,node,dimids)); for(i=0;i<rank;i++) { char* dimname = NULL; OCddsnode dim = dimids[i]; check_err(oc_dimension_properties(link,dim,&size,&dimname)); printf("["); if(dimname != NULL) printf("%s=",dimname); printf("%lu]",(unsigned long)size); if(dimname) free(dimname); } } printf("\n",name); break; case OC_Grid: printf("%s {\n",oc_typetostring(octype)); /* Get the Array field (i.e. subnode zer0) of this Grid */ indent(depth); printf("Array:\n"); check_err(oc_dds_ithfield(link,node,0,&field)); generatedds(link,field,depth+2); /* Print the map fields */ indent(depth); printf("Maps:\n"); for(i=1;i<nsubnodes;i++) { check_err(oc_dds_ithfield(link,node,1,&field)); generatedds(link,field,depth+2); } indent(depth); printf("} %s\n",name); break; } } generateddsattributes(link,node,depth+1); oc_dds_free(link,node); if(name) free(name); } static void generateddsattributes(OClink link, OCddsnode node, int depth) { size_t i,j; size_t nattrs; char tmp[128]; char* aname; char* name; OCtype atomictype; size_t nvalues; char id1[1024]; check_err(oc_dds_attr_count(link,node,&nattrs)); check_err(oc_dds_name(link,node,&name)); check_err(oc_dds_name(link,node,&name)); if(nattrs > 0) { for(i=0;i<nattrs;i++) { char** values; check_err(oc_dds_attr(link,node,i,NULL,NULL,&nvalues,NULL)); values = (char**)malloc(sizeof(char*)*nvalues); if(values == NULL) check_err(OC_ENOMEM); check_err(oc_dds_attr(link,node,i,&aname,&atomictype, &nvalues,values)); indent(depth); printf("%s %s:%s = ", oc_typetostring(atomictype),name,aname); for(j=0;j<nvalues;j++) { if(j > 0) printf(", "); printf("%s",values[j]); } printf(";\n"); free(aname); oc_reclaim_strings(nvalues,values); free(values); } } free(name); } static void check_err(int stat) { if(stat == OC_NOERR) return; fprintf(stderr,"error status returned: (%d) %s\n", stat,ocerrstring(stat)); fflush(stdout); fflush(stderr); exit(1); } static void indent(int depth) { while(depth-- > 0) printf(" "); }
static OCerror FC(OClink link, OCdatanode instance) { int i; OCddsnode node; size_t rank; OCtype octype; size_t nsubnodes; /* Get the OCddsnode object associated with the current instance */ FAIL(oc_data_ddsnode(link,instance,&node)); /* Obtain some information about the node */ FAIL(oc_dds_octype(link,node,&octype)); FAIL(oc_dds_rank(link,node,&rank)); FAIL(oc_dds_nsubnodes(link,node,&nsubnodes)); if(octype == OC_Structure && rank > 0) { size_t dimsizes[OC_MAX_DIMENSIONS]; size_t indices[OC_MAX_DIMENSIONS]; /* records current indices using the odometer code */ /* walk all the elements of the dimensioned Structure */ /* Obtain the dimension sizes of the Structure (via the template node) */ FAIL(oc_dds_dimensionsizes(link,node,dimsizes)); /* Initialize the odometer indices to zero */ for(i=0;i0 */ size_t offset; /* Initialize the odometer indices to zero */ odom_init(rank,indices); /* Obtain the dimension sizes */ FAIL(oc_dds_dimensionsizes(link,node,dimsizes)); /* Compute the cross product */ for(count=1,i=0;i 0) printf("[%d]",(int)nelements); printf("=",(int)nelements); for(p=memory,i=0;i 0) printf(","); switch (atomtype) { case OC_Byte: printf("%hhu",*p); break; case OC_Int16: printf("%hd",*(short*)p); break; case OC_UInt16: printf("%hu",*(unsigned short*)p); break; case OC_Int32: printf("%d",*(int*)p); break; case OC_UInt32: printf("%u",*(unsigned int*)p); break; case OC_Float32: printf("%g",*(float*)p); break; case OC_Float64: printf("%g",*(double*)p); break; case OC_String: case OC_URL: printf("\"%s\"",*(char**)p); break; default: break; } } printf("\n"); } int main(int argc, char **argv) { char* fileurl = NULL; OClink link; OCddsnode dataddsroot, dasroot; OCdatanode rootinstance; if(argv[1] == NULL) { fprintf(stderr,"no file url specified\n"); exit(1); } fileurl = strdup(argv[1]); FAIL(oc_open(fileurl,&link)); FAIL(oc_fetch(link,NULL,OCDAS,0,&dasroot)); FAIL(oc_fetch(link,NULL,OCDATADDS,0,&dataddsroot)); FAIL(oc_merge_das(link,dasroot,dataddsroot)); /* Get the root instance */ FAIL(oc_data_getroot(link,dataddsroot,&rootinstance)); /* apply FC to the instance tree to start because we know that the root is a Dataset => container */ FC(link, rootinstance); oc_root_free(link,dasroot); oc_root_free(link,dataddsroot); /* Also frees the associated data tree */ exit(0); } static void odom_init(size_t rank, size_t* indices) { int i; for(i=0;i =0;i--) { indices[i]++; if(indices[i] < dimsizes[i]) break; if(i > 0) indices[i] = 0; } }