Re: Class Notes for a PL/I Course.

"Giuseppe Vitillaro giuseppe-yuD/ahkh7LvrZ44/[email protected] [H390-MVS]" <[email protected]> Fri, 4 Oct 2019 18:53:26 +0200 (CEST)
Newsgroups gmane.comp.emulators.hercules390.mvs
Message-ID <[email protected]>
On Fri, 4 Oct 2019, Bernd Oppolzer berndoppolzer-/[email protected] [H390-MVS] wrote:

> Am 03.10.2019 um 19:46 schrieb Giuseppe Vitillaro giuseppe-yuD/ahkh7LvrZ44/[email protected] 
> [H390-MVS]:
>> 
>>
>>  In the meanwhile I did some progress with
>>  my VS2FB/FB2VS exercise.
>>
>>  The ability of PL/I(F) to manage at the
>>  same time a FILE STREAM and a FILE RECORD
>>  is a wonderful feature, which makes
>>  storing a VS dataset into an FB(80)
>>  dataset as simple as, I guess, it can
>>  be under MVS.
>>
>>  What I've now, basically, are two I/O loops,
>>  one for storing a VS FILE RECORD into an
>>  FB FILE STREAM:
>>
>>  DECLARE OUTF FILE STREAM OUTPUT;
>>  DECLARE INPF FILE RECORD INPUT ENV(VS);
>>  ....
>>  LP = ADDR(L);
>>  DO WHILE(TRUE);
>>  READ FILE(INPF) INTO(BUF);
>>  L = LENGTH(BUF);
>>  PUT FILE(OUTF) EDIT(SP,BUF) (A(2),A);
>>  END;
>>
>>  and the other one for restoring the FB
>>  FILE STREAM back into a VS FILE RECORD:
>>
>>  DECLARE INPF FILE STREAM INPUT;
>>  DECLARE OUTF FILE RECORD OUTPUT ENV(VS);
>>  ,,,
>>  LP = ADDR(L);
>>  DO WHILE(TRUE);
>>  GET FILE(INPF) EDIT ( SP ) ( A(2) );
>>  IF ( L = 0 ) THEN GOTO EOF;
>>  GET FILE(INPF) EDIT ( BUF ) ( A(L) );
>>  WRITE FILE(OUTF) FROM ( BUF );
>>  END;
>>
>>  where the last FB record has a plain
>>  zero length marking the logical EOF
>>  of the VS dataset.
>>
>>  The PL/I(F) limitation to a "CHAR(32767) VARYING"
>>  should not be a problem here, I hope, because the max
>>  VS LRECL I may expect is actually 32760, isn't it?
>>
>>  The only "glitch" in this code I can foresee, which
>>  actually works over an IEBCOPY output
>>  VS dataset, is in the conversion between
>>  the "FIXED BIN(15)" variable L and its
>>  corresponding "CHARACTER(2)" SP binary
>>  representation.
>>
>>  What I'm doing now, which I guess is
>>  really my C habit, is using the LP pointer
>>  in this way:
>>
>>  DECLARE L FIXED BIN(15);
>>  DECLARE LP POINTER;
>>  DECLARE SP CHARACTER(2) BASED(LP);
>>
>>  which makes SP overloading the same memory
>>  area of the L record lenght variable once
>>  LP is initialized to the ADDRess of L.
>>
>>  Is there any "clean way" to make such
>>  a conversion with PL/I (F), at runtime?
>> 
>
> No, no clean way IMO.
> You are reading a CHAR(2) field using stream I/O,
> which is in fact a BIN FIXED (15) length field, and you need
> to use it as a BIN FIXED (15) field. This is not really a conversion;
> the overlay that you are using is IMO the right thing to do.
>
> Using newer PL/1 compilers, I would write something like
>
> DECLARE L FIXED BIN(15);
> DECLARE SP CHARACTER(2) BASED(ADDR(L));
>
> but PL/1 (F) does not support this.

Understood, Bernd. It looks at the end
pointers are pointers, in PL/I as in C ;-)

>
>>  Beside that, now, I can easily store
>>  my IEBCOPY datasets into FB datasets, possibly members
>>  of a PDS FB(80), and trasfer them to my Linux filesystems,
>>  a simple way to store backups of my PDS
>>  under MVS and Linux, without using XFER370
>>  and its bad habits, which I really hate ;-)
>>
>>  By the way, it is fast as an hell, with a large
>>  enough FB(80) BLKSIZE, almost as XFER370.
>>
>>  Any way to make it faster with PL/I(F)?
>> 
>
> No, I don't think so.
>
> It took me some time to understand that you are using
> stream I/O and the GET/EDIT and PUT/EDIT instructions
> to write buffers to the FB 80 dataset that are far larger than
> 80 bytes, and that the GET and PUT statement will in fact
> do the folding and unfolding of the large buffers to the
> 80 byte records for you. This is great ... this solution would
> not have come to my mind.

Well, reading about STREAMS, and being a STREAM a stream,
I supposed they would have done under MVS what they do under any other OS,
access a dataset as a stream of unstructured bytes.

Well they really do ;-) ... even under MVS ... at least from PL/I.

Not sure if actually streams are implemented in the PL/I libraries,
or rather directly in an MVS access methods.

Guess what? I already have a "FILE RECORD FB(80)" implementation for 
VS2FB, which of course has a definitely more complex logic, because
of what you have named "folding and unfolding".

Not sure is fully debugged, but it looks performances are
quite the same, using WRITE rather than PUT/EDIT and manually
implementing the unfolding logic.

It looks the PL/I code is not such far from the performances
which may have been expected from an assembler code, probably
in the order of one half. I actually coded VS2FB in assembler
one year ago, so, I'm in the position to compare them ;-)

But coding the "folding/unfolding" logic is not such easy,
code is longer, more involved, at least my PL/I and assembler codes,
a mess of buffer offsets computations.

Coded as STREAM/RECORD makes a so simple code, as much
of the dirty work is done under the cover by the PL/I
runtime.

And it would actually work, coded as is now, for
any type of MVS dataset, as the the logic of the
program doesn't make actually any assumption on
the RECFM of the datasets, leaving the definition
to the DCB stored by MVS with the dataset or into the
DD card.

A quite good degree of freedom, isn't it?

Beside that, this particular case, I understand
why, in common PL/I codes, STREAMs are barely
used.

Used in the wrong way, without understanding you
may lose the record structure of a dataset,
it may become a nightmare or even impossible to recover the data
stored into a dataset.

But not for this case. In this case actually the STREAM
is used to "preserve" the record structure across
different platforms.

What XFER370/RECV370 already do, actually.

>
> In most companies that I worked, stream I/O was always forbidden
> for production programs; it was only allowed for test output.
> Even listings had to be produced using record output in most companies
> (using RECFM FBM and machine control characters). So I would
> come up with a solution using record I/O for the FB 80 file and
> probably doing the splitting of the larger buffer into 80 byte pieces
> (and joining at the other side) using handwritten PL/1 logic.
> Don't know, if this would make a difference in performance. Maybe.
>
> It would be interesting to see, where the length fields are in your
> FB 80 dataset. I guess, they can appear at any column, depending
> on the length of the original VS records.

Almost "random columns" Bernd, you right, at least in VS datasets
generated from IEBCOPY, the datasets to which I'm actually
interested.

>
> If I did the splitting myself, I would probably use a file format,
> where the length fields are always at position 1 and 2 of the FB80
> records. This of course means
>
> - that the VS buffer must be split in pieces of 78 bytes length
>
> - that you can use the length fields of the records after the first one
> for some additional control (e.g. repeating the total length of the VS
> record, or the remaining length, or marking the end of the total record)
>
> - that you have some unused space at the end of the record in
> the general case
>
> In this case, you don't need no special EOF record using a zero length field.

Yep, but in this case I would waste a LOT of space and
IEBCOPY PDS output unload datasets may be really large.

Wasting a couple of bytes at the end of the FB(80) dataset
is not such a nightmare and, wrote in this logic, should
be a correct choice of a logical EOF. Am I right betting
a variable length dataset can't have a zero length records?

In this way a V[SB] dataset can be packed in an F[B] dataset
wasting at most LRECL-3 bytes, isn't it?

>
>
>>
>>  Just for reference, if anyone would even wish
>>  to a have a look to my poor PL/I coding the
>>  JCL test streams are stored at these URLs:
>> 
>> http: //www.vitillaro.org/curr/VS2FB
>> http: //www.vitillaro.org/curr/FB2VS
>>
>>  Peppe.
>> 
>>
>> 
>
> Very nics, congrats :-)

Thanks Bernd, you are too kind with me ;-)

These are just my first attempts to write PL/I code,
but I'm not a newby in programming of course.

>
> I am coding in PL/1 almost every day (for money) ... :-)

Too old for thinking to switch to another job,
PL/I code for me is just for the fun of my aging ;-)

>
> there are only two minor remarks, both are a matter of personal style:
>
> a) DECLARE is abbreviated DCL most of the time

Got it, Bernd, I'll try to use DCL in my next codes.

I'm too young as a PL/I coder to have actually developed
any style.

>
> b) ENDFILE processing is normally done using a BIT switch, avoiding the GOTO.
>
> Example:
>
> DCL SW_EOF BIT(1) INIT ('0'B);
> ON ENDFILE(INPF)? SW_EOF = '1'B;

Yep, I've seen it and I'll probably use
this construct, but in the VS2FB code, GOTO
makes the code so compact ;-) ... and leave
the pleasure to a C programmer, which almost
never code a "goto", to write one, once in
his life ;-)

By the way. PL/I(F) doesn't have LEAVE/CYCLE
to control a WHILE loop, isn't it? So a GOTO
is a must for these case?

>
> ....
>
> DO WHILE (^ SW_EOF);
> ??  READ FILE(INPF) INTO (BUF);
> ??  ...
>
> END;
>
> /* processing continues here at EOF */
>

The problem I'm facing now, is to retrieve
from PL/I (F) the LRECL/BLKSIZE of an IEBCOPY
dataset at "runtime".

Infact restoring an IEBCOPY dataset from
a packed F[B](80) dataset requires to restore
also the original LRECL/BLKSIZE, otherwise
IEBCOPY would miserably fail at load time,
beside the F[B](80) dataset being basically
correct.

For what I've seen there are not builtin
functions for that, even in modern PL/I
implementation.

But I've the C code to access DDNAMES
for an open dataset, using MVS control blocks.

I guess the same logic, starting from PSA,
at absolute address ZERO, and following down
the chain of control blocks to JFCB can be
also implemented in PL/I (F), as I've seen
it implemented in modern PL/I implementations,
under Z/OS.

Does it happen you have a PL/I implementation
already available, Bernd?

Thanks again for your nice words and your time,

Peppe.