Wednesday, September 13, 2017

Writing to a User Space

using api to write to user space

I have written in the past about using User Spaces with List APIs. These earlier examples showed how I can retrieve data from a User Space, but how would I write or update my own User Space?

A User Space is like a data area, only bigger. While the maximum size of a data area is 2,000 bytes, a User Space can be up to 16,776,704 bytes. And I must use APIs to put and retrieve data in and out of a User Space.

Why would I use a User Space? They can be anything I want them to be.

  • Passing large number of parameters between programs
  • Share data between more than more than one program, I could use it to share data between more than one procedure
  • Snapshot of a file record (or table row)
  • I know of one software vendor that stores SQL statements in User Spaces that are later executed

This example is going to be a lot simpler than any of the above. I am going to have two RPG programs.

  1. Write data to User Space
  2. Retrieves data from User Space

Let's get started with the definitions in the first program.

01  **free
02  ctl-opt option(*nodebugio:*srcstmt:*nounref) ;

03  /copy qsysinc/qrpglesrc,qusec    //Error DS for APIs

04  dcl-pr CrtUserSpace extpgm('QUSCRTUS') ;
05    *n char(20) const ;  // Name
06    *n char(10) const ;  // Attribute
07    *n int(10) const ;   // Initial size
08    *n char(1) const ;   // Initial value
09    *n char(10) const ;  // Authority
10    *n char(50) const ;  // Text
11    *n char(10) const ;  // Replace existing
12    *n char(32767) options(*varsize:*nopass) ;  // Error feedback
13  end-pr ;

14  dcl-pr ChgUserSpace extpgm('QUSCHGUS') ;
15    *n char(20) const ;   // Name
16    *n int(10) const ;    // Starting position
17    *n int(10) const ;    // Length of data
18    *n char(100) const ;  // Input data
19    *n char(1) const ;    // Force changes to auxillary storage
20    *n char(32767) options(*varsize:*nopass) ;  // Error feedback
21  end-pr ;

22  dcl-ds TestDs qualified ;
23    Str char(4) inz('str:') ;
24    Data char(50) ;
26    End char(4) pos(97) inz(':end') ;
27  end-ds ;

28  dcl-s UserSpaceName char(20) inz('TEST      QTEMP') ;

Line 1: I only ever program in totally free RPG these days.

Line 2: My favorite control options.

Line 3: I am using this /COPY to copy the layout for the API error data structure, QUSEC, into the program.

Lines 4 – 13: This the procedure definition for the API, QUSCRTUS, to create the User Space.

Lines 14 – 21: This is the procedure definition for the API, QUSCHGUS, to change the User Space.

Line 22 – 27: As User Spaces do not come with field or column definitions I am using the data structure to define what I will be writing to the User Space.

Line 28: I am going to use this variable to contain the name of the User Space I will be creating and using. The User Space name is "qualified", in other words it needs to be the User Space name (positions 1 – 10) followed by the library (positions 11 – 20).

And now onto the interesting part of the program.

29  CrtUserSpace(UserSpaceName:'':1:x'00':'*ALL':
                 'User Space used for test':
                 '*YES':QUSEC) ;

30  TestDs.Data = 'First attempt' ;
31  ChgUserSpace(UserSpaceName:1:100:TestDs:'0':QUSEC) ;

32  TestDs.Data = 'Second attempt' ;
33  ChgUserSpace(UserSpaceName:101:%size(TestDs):
                 TestDs:'0':QUSEC) ;

34  *inlr = *on ;

Line 29: The CrtUserSpace (QUSCRTUS) API creates the User Space. The parameters are:

  1. Qualified User Space name
  2. Attribute, I don't care about this so I pass null ( '' )
  3. Initial size, if I pass 1 the user Space will automatically extend itself when needed
  4. I am initializing the User space as hexadecimal 00. I will be using this in the second program
  5. The User Space has authority of *ALL
  6. This is the text that will be used for the User Space's description
  7. Replace existing User Space, as I pass *YES if there is already a User Space with the name in the library it will be deleted and replaced with this new one
  8. Error data structure

line 30: Moving a value to a subfield in the TestDs data structure.

Line 31: The ChgUserSpace (QUSCHGUS) API changes the User Space. I can change an unused part of the User Space and this is the equivalent of a write. The parameters are:

  1. Qualified User Space name
  2. Starting position for the change to start
  3. The length of the data that will be changed
  4. The data that will be moved to the User Space. As TestDs is 100 bytes then this parameter is also 100
  5. I can force the change to the User Space, but as I passed 0 the normal change operation is performed
  6. Error data structure

Line 32: A different value is moved to the data structure's subfield.

Line 33: The ChgUserSpace API is called again. This time start position, second parameter, is 101, and the I am using the %SIZE built in function to determine the length of the data.

After this program has run if I want to see the contents of the User Space I need to use the Dump Object command, DMPOBJ.

  DMPOBJ OBJ(QTEMP/TEST) OBJTYPE(*USRSPC)

The command generates the spool file QPSRVDMP. When I look in spool I can see the contents in the SPACE section of the dump.

SPACE-
 000000      *str:First attempt               *
 000020      *                                *
 000040      *                                *
 000060      *:endstr:Second attempt          *
 000080      *                                *
 0000A0      *                                *
 0000C0      *    :end                        *
 0000E0      *                                *
     LINES  000100   TO   000FFF  SAME AS ABOVE

To ensure that this fits on the page I have removed the hexadecimal part of the dump.

Now for the program that retrieves the data from the User Space.

01  **free
02  /copy qsysinc/qrpglesrc,qusec    //Error DS for APIs

03  dcl-pr RtvUserSpace extpgm('QUSRTVUS') ;
04    *n char(20) const ;   // Name
05    *n int(10) const ;    // Starting position
06    *n int(10) const ;    // Length
07    *n char(100) ;        // Retrieved data
08    *n char(32767) options(*varsize:*nopass) ;  // Error feedback
09  end-pr ;

10  dcl-ds TestDs qualified ;
11    Str char(4) ;
12    Data char(50) ;
13    End char(4) pos(97) ;
14  end-ds ;

15  dcl-s StartPosition int(10) ;
16  dcl-s Maximum int(10) ;

17  Maximum = (%size(TestDs) * 9) + 1 ;

18  for StartPosition = 1 by %size(TestDs) to Maximum ;
19    RtvUserSpace('TEST      QTEMP':StartPosition:
                   %size(TestDs):TestDs:QUSEC) ;

20    if (%subst(TestDs:1:1) = x'00') ;
21      leave ;
22    endif ;
23  endfor ;

24  *inlr = *on ;

Lines 3 – 9: In my previous examples of retrieving data from a User Space I used the QUSPRTUS to get a pointer I could use to retrieve the data. The RtvUserSpace API uses a different API, QUSRTVUS, to get the data.

Lines 10 – 145: This is the data structure I will be placing the retrieved data into.

Lines 15 and 16: Are variables I will be using in a FOR group.

Line 17: I want to perform the FOR group nine times so I need to calculate the maximum possible value of the start position.

Lines 18 – 23: Is the FOR group. I am initializing the variable StartPosition with 1 and then incrementing it by the size (length) of TestDs. This means that StartPosition will be: 1, 101, 201, 301, 401, etc until it is equal to the value of Maximum, 901.

Line 19: The RtvUserSpace (QUSRTVIS) retrieves that data from the User Space. The parameters are:

  1. Qualified User Space name
  2. Starting position for the data to retrieve, which is the value in StartPosition
  3. The length of the data to retrieve. This remains constant as it is the size of TestDs
  4. The data is retrieved into TestDs
  5. Error data structure

Lines 20 – 22: As there is no "end of data" I need a way to determine if there is no more data. If I check the first position of the data structure and it is the value I initialized the User Space to, line 29 in the first program, hexadecimal 00 I know there is no more data to retrieve.

 

In a later post I describe how you can write and retrieve from a User Space using a pointer.

 

You can learn more about this from the IBM website:

 

This article was written for IBM i 7.3, and should work for earlier releases too.

9 comments:

  1. another very powerful tool for the i tool box.

    ReplyDelete
  2. I generally us the QUSPTRUS API to obtain a pointer to the user space and then base my data on that pointer. This allows direct access to the space.

    I also use the QUSCUSAT API to make the user space automatically extendible. That way I can create a user space of that will handle my expected data, but it will automatically extend if the actual amount of data is larger.

    ReplyDelete
  3. "I only ever program in totally free RPG these days." Nice!

    By not giving the fixed format version anymore, you are sending a strong message to fixed format guys, "You are going to miss out a lot of productivity tools if you stick to fixed format".

    Its called "Tough Love" :)

    ReplyDelete
    Replies
    1. No - you saying: We are just a bunch of guy that want to use C, but are not allowed.

      PS: I been using "user spaces" before IBM i or AS/400. John Sears gave us access to it and "user indexes", because IBM would not allow us access to Assembler as we had on S/34 and S/36. We passed data between Interactive and Batch, then back again. It was how we got around the 64k region size and not using call parms.

      Delete
  4. I had put an RFE requesting that future RPG should start with version# instead of **FREE. e.g. **RPG7. However it got rejected. Maybe I should have eloborated further and said it should have a different source type e.g. RPG7 instead of RPGLE, and this source type should process only **FREE. Would you or anyone else support that RFE if I put it?

    ReplyDelete
  5. Several people have pointed out to me in comments here, and in other methods of communication that I should have used pointer the retrieve the data from the user space.

    While using the API I showed in this example is OK to use, I agree that I should have given an example using a pointer. What i will do is to "point" (pun intended) to the post I wrote about using a pointer to retrieve data from a user space and say you that in place of what I have given below, see here.

    ReplyDelete
  6. Nice article. Well done.

    Another use case would be when you share data between an RPG program and a Java program. With the Java Toolbox you can access the userspace in an easy way. The format of the data needs to be known by both parties but that is doable f. e. by using RFML.

    ReplyDelete
  7. A lot of programs look up values in an array. A permanent user space can be used to immediately attach a fully loaded array by resolving a pointer; you can sort with QSORT and search with the binary search API. This cuts out the time to load data into lookup arrays when compile time arrays do not make sense. (The arrays should not be updated in more than one place due to lock issues.)

    I've used this to load shop calendars with work days so that table lookups permit doing work day arithmetic, for example.

    QSORT with a user space is a great way of supporting a subfile that permits the user to sort on any column as well.

    Lynne

    ReplyDelete
  8. Nice to see that I am not the only one who creates my own prototypes to API's with actual English names instead of those cryptic Q-names provided in the QSYSINC library.

    ReplyDelete

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.