In my previous article titled "Cobol and Dynamic Memory Allocation" (January, 24, 2008, article ID 56196 at SystemiNetwork.com), I introduced the use of some C/C++ runtime functions for dynamic storage management that are available for use from an ILE Cobol program. It's my opinion that one of the main difficulties for a Cobol programmer is to understand the particular jargon in which these functions are documented; therefore, this article continues exploring this area, presenting the calloc() function, which is specifically intended for use with array structures, and explaining in detail how to understand the documentation in the System i Information Center at publib.boulder.ibm.com/iseries.
In the System i Information Center, the function prototype for the calloc() API is documented as follows:
#includevoid *calloc(size_t num, size_t size);
In the C style, a function prototype consists of the function return type, the name of the function, and a list of input parameters enclosed within parentheses. The parameters are separated with commas. The preceding function prototype has two parameters: num and size, and both are of type size_t. But what kind of data type is size_t? Think of it as a user-defined data type. In Cobol, a user-defined data type is a 01 level data item that contains the TYPEDEF clause. You can think of it as a general template that describes the format for other data items. In fact, the compiler doesn't reserve static storage for user-defined data types. Here's an example of that:
01 file-status-type TYPEDEF PIC XX.
01 order-file-status TYPE file-status-type.
88 end-of-file VALUE "10".
Now, if Cobol programmers need to know the type definition for the size_t data type, they must search in the QSYSINC/H/STDLIB member. This is the reason the stdlib header file appears in the first line of the prototype. Looking in the STDLIB member, you see that size_t is defined as an unsigned integer data type. The Cobol equivalent for the C/C++ four-byte unsigned integer data type is the 9(009) BINARY or 9(009) COMP-4 (you can check out the complete description of data type compatibility between C/C++ and Cobol in ILE C/C++ Run-Time Library Functions, a link to which I provide at the end of this article). Therefore, we can read the function prototype in this manner: The calloc() function expects two unsigned integer parameters -- num for the number of entries in the array, and size for the size of each entry -- and returns a pointer (the asterisk before the function name) to the allocated storage in the system space (yes, in the C/C++ function prototypes, pointers are declared with asterisks). In addition, the function initializes the reserved block of storage with the value 0. The return value is NULL if there is not enough storage, or if either of the two input parameters is set to 0. In the context of this function prototype, "void" indicates that the return value is a pointer to an unknown data type. It is the responsibility of the application program to define what is pointed to, eliminating any ambiguity.
The input parameters and the return value could be defined in the WORKING-STORAGE SECTION of a Cobol program in this way:
01 number-of-entries PIC 9(009) BINARY. 01 size-each-element PIC 9(009) BINARY. 01 allocated-storage-ptr POINTER.
And the calloc() function can be called in the PROCEDURE DIVISION in this way:
CALL PROCEDURE "calloc"
USING BY VALUE number-of-entries,
BY VALUE size-each-element
RETURNING allocated-storage-ptr
END-CALL.
In the preceding example, the USING BY VALUE phrase in the CALL PROCEDURE statement means that the input parameters are copied to temporary variables, and the addresses of these temporary fields are passed to the called procedure. This way, even when it has modified the value of the received parameters, it does not affect the calling program, because the called procedure has access only to a temporary copy of the sending data items.
As in my previous article, I demonstrate the power of the calloc() function, using it in conjunction with an i5/OS API that returns a variable amount of data. I use the List Jobs (QUSLJOB) API, for returning in a user space the list of all active jobs in my system. Like many list APIs, QUSLJOB provides a generic list header at the beginning of the user space, which provides information such as the offset of the list data section, the number of entries in the list and the size of each entry. For more information about list APIs, check out my article called "Work with List APIs in Cobol" (November, 13, 2006, article ID 53549).
Now, assume that my application needs to load the complete list of active jobs returned by the QUSLJOB API in an array. How many occurrences should I define in it? I can't know in advance. One solution could be to define the array with a number of occurrences capable of allocating what I suppose is the maximum possible number of active jobs in my system, plus an extra space. As this solution wastes a great deal of resources and is not very imaginative, we can term this a bureaucratic solution (somewhere, I've read that these are features of all the bureaucracies around the world). We who are simple taxpayers are forced to find more efficient solutions. Therefore, the solution proposed here includes:
Pointers allow manipulating information from a user space much faster than using callable APIs such as the Retrieve User Space (QUSRTVUS) API. Unlike static program calls for bindable APIs, dynamic program calls for callable APIs use many system resources. Think about it: The complete list of active jobs surely will contain prestart jobs, batch jobs, non-ending jobs, scheduled jobs, multithread jobs, application servers, and so forth. A mid-size System i installation can easily hold hundreds of active jobs. So, the number of entries returned by QUSLJOB can be very large. Having to call the QUSRTVUS API each time I need to obtain a new list entry from the user space isn't too efficient, eh? Therefore, after calling the QUSLJOB API we do the following:
At the end of this article, I provide a link to the downloadable Cobol example that illustrates all the preceding items.
In the LINKAGE SECTION of my program, I defined the array in this way:
01 active-jobs-array.
02 array-element OCCURS 0 TO 5000 TIMES
DEPENDING ON number-list-entries
IN qus-generic-header-0100.
03 job-name PIC X(0010).
03 user-name PIC X(0010).
03 job-number PIC X(0006).
03 job-status PIC X(0010).
03 active-job-stat PIC X(0004).
Notice that the OCCURS DEPENDING ON clause defines the array as a variable-length table and that the object of the DEPENDING ON clause (the number-list-entries field of the generic header) determines the exact number of occurrences of the table. The compiler doesn't allocate storage for this data structure, as long as it has been declared in the LINKAGE SECTION. It can be referenced only after its address has been set to a pointer data item containing a valid address in the system space. In my program, the value of this pointer is obtained from the return value of the calloc() function. And, as long as the program has properly initialized the input parameters, the function allocates the exact amount of storage needed to hold the variable-length array.
Oftentimes, we may have to deal with situations in which the data change their structure during the execution of the program. The number of occurrences in a table can increase or decrease at runtime. When the array grows, we need to allocate more storage to accommodate additional entries. Such situations can be handled more easily by using the realloc() function. This will be the subject of a future article.
Complete information about the APIs used in this article is in ILE C/C++ Run-Time Library Functions Version 5:
http://publib.boulder.ibm.com/infocenter/iseries/v5r4/topic/books/sc415607.pdf [2]
You can download the example Cobol program at:
http://www.pentontech.com/IBMContent/Documents/article/56601_574_dynamem2.zip [3]
Links:
[1] http://systeminetwork.com/author/carlos-balestrazzi
[2] http://publib.boulder.ibm.com/infocenter/iseries/v5r4/topic/books/sc415607.pdf
[3] http://www.pentontech.com/IBMContent/Documents/article/56601_574_dynamem2.zip