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.