Brief: In RPG/400, retrieving data from a user space requires multiple external
API calls, which impacts performance. Now, with ILE RPG pointers, programs can
access this kind of data in a much more efficient manner. This article explains
how you can use ILE RPG pointers to retrieve data from a user space faster than
ever before.
Among the many enhancements made to RPG in V3R1 is support for a new data type
called a pointer. Pointers provide RPG programmers with an efficient new way to
access data. While you probably won't use pointers in every application you
write, they can be extremely useful with certain types of low-level
programming, such as retrieving data from a user space.
You may be familiar with the concept of pointers if you've used other
programming languages, such as C, but there are some slight differences in the
implementation of pointers in RPG. However, even RPG programmers who have never
used pointers before now have the ability to start taking advantage of them. In
this article, I'll explain some of the concepts of pointers, discuss the RPG
implementation, and show you an example of how to use them in an RPG
application.
Pointer Concepts
The simple definition of a pointer is a variable that contains the address of
another variable. A pointer doesn't tell you what you'll find at an address
location; it tells you only where to find it. For this reason, pointers are
often referred to as addresses.
A simple analogy for a pointer is a street address. A street address doesn't
tell you who lives in a particular house, but it does tell you where to find
the house. Using the address, you can determine who lives there by going to the
house and knocking on the door. Pointers work the same way. Since a pointer
stores the address of a particular piece of data, you can use that address to
directly access the data at that address location. Pointers provide you with a
convenient and efficient way to access the data in a user space.
Until V3R1, RPG hasn't had pointers, but OS/400 uses them extensively. One
place in OS/400 where pointers are evident is in parameter passing. When
parameters are passed between RPG or CL programs, they are actually passed as
pointers, not data. I, like many programmers, found this out the hard way. Have
you ever accidentally coded a parameter with the wrong size in the receiving
program and ended up with garbage in an adjacent parameter? That happens
because parameters are passed as pointers. The receiving program goes to the
address of each parameter and processes whatever data it finds there. If you
were to look at the memory locations for the parameters, you would find that
they are stored one right after the other. So if one of the parameters is
defined too large, the program will process multiple parameter values as if
they were a single parameter. Parameters can easily become corrupt if you
aren't careful about matching their sizes.
Pointer Usage
You probably won't want to use pointers in every application you write. The
most likely candidates are programs that use the list application programming
interfaces (APIs). The list APIs produce lists of dataùsuch as fields, members,
and objectsùinto a user space. 1 shows some common list APIs. Using
and objectsùinto a user space. Figure 1 shows some common list APIs. Using
pointers facilitates the process of extracting the list data out of the user
space.
Before I discuss how to extract data from a user space using pointers, let's
first look at how the list APIs organize the data in a user space. 2
first look at how the list APIs organize the data in a user space. Figure 2
shows the general layout of this type of data. As you can see, the user space
is broken down into four sections: generic header, input parameter, header, and
list data. The generic header section is always found at the beginning of the
user space and contains (among other things) the addresses of the other three
sections.
The input parameter section contains a copy of the parameters passed to the
list API. This section is not usually needed in the calling application program
and is therefore generally not extracted from the user space. The header
section (not to be confused with the generic header) contains information
related to the list. For example, the List Fields API places information such
as the file type, record format, and record length in this section. The list
data section is where the actual list is stored. This section is broken down
into individual entries similar to records in a file.
Prior to the existence of pointers in RPG, the common technique to extract data
from a user space was to call the Retrieve User Space (QUSRTVUS) API. This API
needed to be called repeatedly to retrieve the individual sections of the user
space as well as each entry in the list data section. The multiple external API
calls slowed down the performance of the application. In addition, the Retrieve
User Space API copied the data from the user space to a variable in the
program.
RPG programs can now use pointers instead. Using pointers eliminates the need
to perform repetitive external API calls and has the added benefit of being
able to go to the memory location where the data exists and process it directly
without having to first copy it into a variable.
An Example
Let's look at an example of an application that uses pointers. I chose the List
Fields (QUSLFLD) API, but I could have chosen any list API to illustrate this
technique. The List Fields API produces a list of fields from a database file
and places them into a user space. In this example, I've written a command
called List Fields (LSTFLD). This command accepts a database file as a
parameter and produces a screen similar to the one shown in 3. This
parameter and produces a screen similar to the one shown in Figure 3. This
screen lists the fields in the selected file along with their attributes and
text.
The example consists of typical components of any utility applicationùa command
(4), a CL program (5), a display file (6), and an ILE RPG
(Figure 4), a CL program (Figure 5), a display file (Figure 6), and an ILE RPG
program (7). Since the focus of this article is on pointers, I'll
program (Figure 7). Since the focus of this article is on pointers, I'll
discuss only the RPG program. I've supplied the other components so you can use
this application as a starting point for a similar application using one of the
other list APIs. (You can download this code by calling MC-BBS.)
Using pointers in an RPG program requires several pieces of code. The first is
the data definition of the pointer, the second is a data structure or
standalone field containing the BASED keyword, the third is a call to the
Retrieve Pointer to User Space (QUSPTRUS) API, and the fourth is the use of the
address (%ADDR) built-in function. (For more information on the ILE RPG
techniques, refer to the related reading listed at the beginning of this
article.)
At Label A in 7, you can see the data definition of three pointers:
At Label A in Figure 7, you can see the data definition of three pointers:
SpacePtr, HeaderPtr, and ListPtr. In ILE RPG, you define pointers by specifying
an asterisk as the data type (position 40) on a data specification. In this
example, I've coded these pointers as standalone fields as designated by the S
in position 24. You can also code pointers as subfields of a data structure.
Label B shows three data structures: UserSpace, Header, and List. All three
data structures are based on (linked to) a pointer variable that holds the
address of an area of memory. In this case, the area of memory will contain a
portion of a user space. The BASED keyword provides the link. Notice that the
data structure UserSpace is based on pointer variable SpacePtr, Header is based
on HeaderPtr, and List is based on ListPtr. When a valid address is assigned to
one of the pointer variables, the data structure based on the pointer variable
overlays the data contained at that memory address. The data structure and its
subfields can then be used to reference the data beginning at the location
contained in the pointer variable. Before any of the fields in the data
structure can be used, the basing pointer must be assigned a valid address;
otherwise, an exception error is generated.
At label C in 7, the program calls the Retrieve Pointer to User Space
At label C in Figure 7, the program calls the Retrieve Pointer to User Space
API. This API has only two parametersùthe name of a user space (SpaceName) and
the name of a pointer (SpacePtr). After the call to this API, the pointer is
set to the address of the beginning of the user space. This essentially
overlays the data structure on top of the user space data since they both now
begin at the same storage address. All of the subfields of data structure
UserSpace are then available to the program. Among these subfields is the
definition of a large array called Data, which is composed of single byte
elements. As you'll see, this array is used to retrieve the address of the
other sections of the user space.
The code at Label D in 7 shows the use of the %ADDR built-in function.
The code at Label D in Figure 7 shows the use of the %ADDR built-in function.
This function retrieves the address of a storage location and places it into a
pointer. Here, the function is used to retrieve the address of the beginning of
the header section of the user space. The Data array is used to locate the
beginning byte of the header section. That address is placed into the pointer
called HeaderPtr. This overlays the Header data structure on top of the header
section of the user space since they now share the same storage address. Since
the Header data structure is based on this pointer, all of the subfields become
available.
The program then drops into a loop to retrieve each of the entries in the list
data section. During the first iteration, the statement at Label E in 7
data section. During the first iteration, the statement at Label E in Figure 7
retrieves the address of the first element of the list data section. The Data
array is used to locate the address of the beginning byte of the entry. That
address is placed into the pointer called ListPtr (the based-on pointer for the
List data structure). The List data structure now overlays the first entry in
the list data section of the user space as illustrated in 8 (page 112).
the list data section of the user space as illustrated in Figure 8 (page 112).
Before the next iteration of the loop, the offset within the Data array is
incremented by the size of the list entry (see Label F in 7). The new
incremented by the size of the list entry (see Label F in Figure 7). The new
offset value allows the program to retrieve the address of the next entry (see
Label E in 7). The loop is repeated until all of the entries in the list
Label E in Figure 7). The loop is repeated until all of the entries in the list
have been processed.
One Final Point
As you've seen, pointers can be used effectively in ILE RPG to process data
from a user space. This example could have been simplified, though, if IBM had
taken a more traditional approach to pointer implementation in RPG. In other
languages, such as C, it's common to simply increment a pointer to get to
another storage location. Unfortunately, IBM doesn't allow pointer math in ILE
RPG. This restriction is the reason for the large Data array in this example.
By not allowing pointer math, it often becomes necessary to find alternative
methods of retrieving address locations. Perhaps the next release of RPG will
remove this limitation. Until then, this method is still a big improvement over
the method we used prior to having pointers in RPG.
Robin Klima is a senior technical editor for Midrange Computing.
Reference
ILE RPG/400 Reference (SC09-1526-00, CD-ROM QBKAQE00).
Related Reading
An Introduction to
ILE RPG: Part 1
March 1994
An Introduction to
ILE RPG: Part 2
April 1994
An Introduction to
ILE RPG: Part 3
May 1994
An Introduction to
ILE RPG: Part 4
June 1994
An Introduction to
ILE RPG: Part 5
July 1994
Pointers in ILE RPG
Figure 1Common List APIs
QSRLSAVF List Save Files QDCLCFGD List Configuration Descriptions QUSLMBR List Database File Members QDBLDBR List Database Relations QUSLFLD List Fields QUSLRCD List Record Formats QMHLJOBL List Job Log Messages QMHLSTM List Nonprogram Messages QUSLOBJ List Objects QEZLSGNU List Signed-On Users QPMLPFRD List Performance Data QBNLPGMI List ILE Program Information QBNLSPGM List Service Program Information QSYLAUTU List Authorized Users QSYLATLO List Objects Secured by an Authorization List QSYLOBJP List Objects that Adopt Owner Authority QSYLOBJA List Objects a User is Authorized To QSYLUSRA List Users Authorized to an Object QUSLSPL List Spooled Files QWCLASBS List Active Subsystems QUSLJOB List Jobs QWCLSCDE List Job Schedule Entries QWCLOBJL List Object Locks QWDLSJBQ List Subsystem Job Queues
Pointers in ILE RPG
Figure 2User Space Organization for List APIs
UNABLE TO REPRODUCE GRAPHICS
Pointers in ILE RPG
Figure 3List Fields Screen
UNABLE TO REPRODUCE GRAPHICS
Pointers in ILE RPG
Figure 4The LSTFLD Command
/*===============================================================*/ /* To compile: */ /* */ /* CRTCMD CMD(XXX/LSTFLD) PGM(XXX/FLD001CL) + */ /* SRCFILE(XXX/QCMDSRC) */ /* */ /*===============================================================*/ CMD PROMPT('List Fields') PARM KWD(FILE) TYPE(QUAL) MIN(1) PROMPT('File') PARM KWD(RCDFMT) TYPE(*NAME) DFT(*FIRST) + SPCVAL((*FIRST)) PROMPT('Record format') QUAL: QUAL TYPE(*NAME) LEN(10) QUAL TYPE(*NAME) LEN(10) DFT(*LIBL) + SPCVAL((*LIBL)) PROMPT('Library')
Pointers in ILE RPG
Figure 5CL Program FLD001CL
/*===============================================================*/ /* To compile: */ /* */ /* CRTCLPGM PGM(XXX/FLD001CL) SRCFILE(XXX/QCLSRC) */ /* */ /*===============================================================*/ PGM PARM(&FILE &RCDFMT) DCL VAR(&FILE) TYPE(*CHAR) LEN(20) DCL VAR(&RCDFMT) TYPE(*CHAR) LEN(10) DCL VAR(&MSGID) TYPE(*CHAR) LEN(7) DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(80) /* Send all errors to error handling routine */ MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR)) /* Create user space if necessary */ CHKOBJ OBJ(QTEMP/FLD001US) OBJTYPE(*USRSPC) MONMSG MSGID(CPF9801) EXEC(CALL PGM(QUSCRTUS) + PARM('FLD001US QTEMP' ' ' 32767 ' ' + '*ALL' 'User space for LSTFLD command')) /* Call the List Fields API */ CALL PGM(QUSLFLD) PARM('FLD001US QTEMP' + 'FLDL0100' &FILE &RCDFMT '0') /* Call program to display fields */ CALL PGM(FLD001RG) /* Branch around error handling routine */ GOTO CMDLBL(ENDPGM) /* Error handling routine */ ERROR: RCVMSG MSGTYPE(*EXCP) MSGDTA(&MSGDTA) MSGID(&MSGID) SNDPGMMSG MSGID(&MSGID) MSGF(QCPFMSG) MSGDTA(&MSGDTA) + MSGTYPE(*ESCAPE) ENDPGM: ENDPGM
Pointers in ILE RPG
Figure 6Display File FLD001DF
*=============================================================== * To compile: * * CRTDSPF FILE(XXX/FLD001DF) SRCFILE(XXX/QDDSSRC) * *=============================================================== *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 A DSPSIZ(24 80 *DS3) A PRINT A CA03(03) A CA12(12) A R DSPSFL01 SFL A FLDNAME 10A O 7 2 A FLDLENGTH 5Y 0O 7 13EDTCDE(3) A FLDDECPOS 3Y 0O 7 19EDTCDE(3) A N60 DSPATR(ND) A DATATYPE 1 O 7 26 A TEXT 50A O 7 31 A R DSPCTL01 SFLCTL(DSPSFL01) A SFLSIZ(0016) A SFLPAG(0015) A OVERLAY A SFLDSP A SFLDSPCTL A N03 SFLEND(*MORE) A 1 35'List Fields' A DSPATR(HI) A 3 2'File . . . :' A FILENAME 10A O 3 16 A 3 29'Record format . . :' A RCDFORMAT 10A O 3 50 A 4 2'Library . . :' A LIBRNAME 10A O 4 16 A 4 29'File type . . . . :' A FILETYPE 10A O 4 50 A 6 2'Field Len' A DSPATR(HI) A 6 20'Dec Type Text' A DSPATR(HI) A R DSPRCD01 A 23 2'F3=Exit F12=Cancel' A COLOR(BLU) *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7
Pointers in ILE RPG
Figure 7ILE RPG Program FLD001RG
*========================================================================= * To compile: * * CRTBNDRPG PGM(XXX/FLD001RG) SRCFILE(XXX/QRPGLESRC) * *========================================================================= *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8 FFLD001DF CF E WORKSTN SFILE(DSPSFL01:Recno) D SpacePtr S * D HeaderPtr S * D ListPtr S * D UserSpace DS BASED(SpacePtr) D Data 1 DIM(32767) D OffSetHdr 117 120B 0 D OffSetLst 125 128B 0 D NumLstEnt 133 136B 0 D EntrySize 137 140B 0 D Header DS BASED(HeaderPtr) D FileName 1 10 D LibrName 11 20 D FileType 21 30 D RcdFormat 31 40 D List DS BASED(ListPtr) D FldName 1 10 D DataType 11 11 D Length 21 24B 0 D Digits 25 28B 0 D DecPos 29 32B 0 D Text 33 82 D SpaceName S 20 INZ('FLD001US QTEMP') D Recno S 5 0 * Retrieve pointer to user space C CALL 'QUSPTRUS' C PARM SpaceName C PARM SpacePtr * Get heading information C EVAL HeaderPtr = %ADDR(Data(OffSetHdr + 1)) * Repeat for each entry in the List Data section C DO NumLstEnt * Get detail information C EVAL ListPtr = %ADDR(Data(OffSetLst + 1)) * Load field Length and Decimal positions C IF Digits = 0 C EVAL FldLength = Length C EVAL *IN60 = *OFF C ELSE C EVAL FldLength = Digits C EVAL FldDecPos = DecPos C EVAL *IN60 = *ON C ENDIF * Write subfile record C EVAL Recno = Recno + 1 C WRITE DSPSFL01 * Get location of next entry C EVAL OffSetLst = OffSetLst + EntrySize C ENDDO * Write screen C WRITE DSPRCD01 C EXFMT DSPCTL01 C EVAL *INLR = *ON *. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7 ...+... 8
LATEST COMMENTS
MC Press Online