× The internal search function is temporarily non-functional. The current search engine is no longer viable and we are researching alternatives.
As a stop gap measure, we are using Google's custom search engine service.
If you know of an easy to use, open source, search engine ... please contact support@midrange.com.


  • Subject: Re: Dynamic arrays
  • From: "Simon Coulter" <shc@xxxxxxxxxxxxxxxxx>
  • Date: Fri, 30 Apr 99 21:48:00 +1000


Hello Lloyd,

This isn't really all that difficult.  The simple solution (and the one most 
familiar to RPG 
programmers) is to create an array with sufficient space for the most likely 
number of 
entries.  If the range is 40 to 60 entries then create an array with room for 
70 (and code 
some sort of limits test to warn when the array gets ful).

However, you wanted to know how to do this dynamically.  There are two 
possiblities that 
spring to mind:

1/ Create a multiple occurrence data structure (MODS) describing the record 
(easy here since 
it is externally described).  The numer of occurrences can be the largest 
amount supported by 
the language -- RPG IV == 32,767

2/ Create an array with each element being the length of a record and 32,767 
elements.

The trick is that both the array and the data structure are based on a pointer. 
 The compiler 
will define a view of storage but will not allocate storage.  That is your 
responsibility.

Use the ALLOC function to create enough space for the expected amount of 
storage.  The value 
used is the length of each record multiplied by the number of expected records.

Your program needs to track how much room has been allocated and how much has 
been used.  
Assume you allocate storage for 40 entries.  When you fetch the 41st record you 
use REALLOC to 
increase the storage space by some amount (maybe record length times 10).  When 
you fetch the 
51st record you call REALLOC again.

When you finsh processing the array or MODS you use DEALLOC to release the 
storage.

Note that there are APIs which will allow dynamic storage manipulation so this 
technique is 
available to other languages also. CEEGTST == ALLOC; CEECZST == REALLOC; 
CEEFRST == DEALLOC

Examples of this technique can be found in any decent book on C programming and 
every book 
shop has tens of these.  Translation table follows:

        ALLOC   ==      malloc() or calloc()
        REALLOC ==      realloc()
        DEALLOC ==      free()

but they behave in a similar fashion.

The big problem from an RPG perspective is that you cannot define an open-ended 
array or MODS.  
You have to tell the compiler the number of elements required and you can't 
change it at run 
time.  One solution is to use the languages maximum supported value since based 
structures 
don't occupy storage.  Another is to only use a pointer with a single record 
defined and bump 
it up through the allocated storage effectively managing your own array.

So for a very quick, off the tips of my fingers, spot the deliberate mistake, 
RPG IV example:

FWORKFILE  IFE                          DISK
 * Declare some magic values
D $DftNbrRcd    C                                       CONST(40)
D $IncNbrRcd    C                                       CONST(10)
D $MaxNbrRcd    C                                       CONST(32767)
 * Declare some work fields
D @DynPtr       S                               *
D CurOccur      S                                  5  0
D UsdOccur      S                                       LIKE(CurOccur)
D AvlOccur      S                                       LIKE(CurOccur)
D                                                       INZ($DftNbrRcd)
D Record        S                                       EXTNAME(WORKFILE)
 * Now we define two views of storage both based on the same pointer.  We can 
treat the
 * the storage as a multiple occurence data structure or as an array.  We 
cannot yet
 * reference the storage because it has not been allocated.
D Struct        DS                                      BASED(@DynPtr)
D                                                       EXTNAME(WORKFILE)
D                                                       PREFIX(W_)
D                                                       OCCUR($MaxNbrRcd)
D Array         S                                       BASED(@DynPtr)
D                                                       LIKE(Struct)
D                                                       DIM($MaxNbrRcd)
D StgAmt        S                                  5  0
 * Now we allocate the expected amount of storage
C                       EVAL    StgAmt = $DftNbrRcd * %SIZE(Struct)
C                       ALLOC   StgAmt          @DynPtr
 * Starting at the first occurrence of the MODS, fill it with data from WORKFILE
C                       READ    WORKFMT                             99
C                       DOW     *IN99 = *OFF
C                       EVAL    CurOccur = CurOccur + 1
C                       OCCUR   Struct
C                       MOVE    Record          Struct
C                       READ    WORKFMT                             99
 * If we have more data and we have filled the available space ...
C                       IF      ((*IN99 = *OFF) AND (CurOccur = AvlOccur))
 *   ... get some more space.
C                       EVAL    StgAmt = StgAmt + ($IncNbrRcd * %SIZE(Struct))
C                       REALLOC StgAmt          @DynPtr
C                       EVAL    AvlOccur = AvlOccur + $IncNbrRcd
C                       ENDIF
 * ... and around we go again to fill the next occurrence
C                       ENDDO
 * Save the num
r of actual elements filled with data
C                       EVAL    UsdOccur = CurOccur
 * The MODS is filled with data and we can start using it (the above code could 
all go in
 *   the *INZSR routine.
                        :
 * Code which does stuff using the MODS and array goes here
                        :
 * Now we release the allocated storage, clean up, and go home
C                       DEALLOC                 @DynPtr
C                       SETON                                   LR
C                       RETURN

You would need to add error handling to cope with the situation where you had 
filled all 
32,767 elements in the array/MODS.  I leave that as an exercise for the reader.

Remember that any LOOKUP operation on the array is linear and a binary search 
should 
be considered for more than 100 elements.  See the archive for a discussion on 
this topic.  
You may find in practice that a linear search on a large number of elements is 
slower than a 
CHAIN to a record (and index) already in main store.  Also data in the array 
does not 
necessarily reflect the current data values in the DB file.

Another alternative, particularly if the data in the WORKFILE is going to be 
shared by 
multiple jobs is to populate a user space with the data and share that user 
space amongst the 
jobs.  In this case the basing pointer is set to address the user space.  The 
QUSPTRUS API can 
be used to set the address. A common job would be used to load the user space 
(or simply use 
the space instead of a file).

The advantage to these techniques over simply using the database file is 
reduced I/O (even 
when the file is completely in main store (through paging or SETOBJACC).  
Dynamically 
allocated storage comes from the HEAP (and there are flavours of HEAP storage 
too) which is 
part of the job and therefore always in main store, the user space does need to 
be 
demand-paged but there is less overhead than with DB files and it does give 
access to 16MB of 
space.  It will always be faster to access program storage than DB storage 
primarily due to 
avoding all the DB code.

Of course benchmarks may need to be performed to determine which technique 
works best in your 
situation. (Hah! the ever useful escape clause strikes again!)

Regards,
Simon Coulter.

«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»
«» FlyByNight Software         AS/400 Technical Specialists       «»
«» Eclipse the competition - run your business on an IBM AS/400.  «»
«»                                                                «»
«» Phone: +61 3 9419 0175      Mobile: +61 0411 091 400           «»
«» Fax:   +61 3 9419 0175      mailto: shc@flybynight.com.au      «»
«»                                                                «»
«» Windoze should not be open at Warp speed.                      «»
«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»«»
//--- forwarded letter -------------------------------------------------------
> X-Mailer: Forte Agent 1.5/32.451
> Date: Thu, 29 Apr 99 23:50:31 +0000
> From: lgoodbar@watervalley.net
> To: midrange-l@midrange.com
> Reply-To: MIDRANGE-L@midrange.com
> Subject: Dynamic arrays

> 
> I have a work file (about 50 records) that is accessed very often by a RPG4
> program. I was thinking about reading the file into an array to make lookups
> faster. The problem is the file changes in size; sometimes it's 50 records,
> sometimes 60, or 40, etc. I'd like to create a dynamically-sized array at
> runtime. I briefly looked at the ALLOC/DEALLOC/REALLOC opcodes, but they
> really didn't make much sense.
> 
> Is there a relatively easy way of creating dynamic arrays in RPG, or am I
> forced to create an arbitrary upper limit?
> 
> Thanks,
> Loyd

+---
| This is the Midrange System Mailing List!
| To submit a new message, send your mail to MIDRANGE-L@midrange.com.
| To subscribe to this list send email to MIDRANGE-L-SUB@midrange.com.
| To unsubscribe from this list send email to MIDRANGE-L-UNSUB@midrange.com.
| Questions should be directed to the list owner/operator: david@midrange.com
+---


As an Amazon Associate we earn from qualifying purchases.

This thread ...


Follow On AppleNews
Return to Archive home page | Return to MIDRANGE.COM home page

This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].

Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.