× 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.




Hi Shannon,

take a look at this and give me your
opinion on whether or not I coded this correctly for this bit of code
(please):

The way you're coding it will run the risk of corrupting the data at the end of the file because it doesn't keep track of the length of the data you read.

When you read the data by calling fread(), how much data gets loaded into your 'outBuffer' variable? You have the variable defined to be 32767 -- which is fine, but what if there's less than 32767 bytes left to read in th file?

When you work with binary data, it's important to track the length. You can't trim blanks (which are x'40') or look for x'00' to determine the length since these are valid values in binary data -- and therefore they don't mean anything. They might just be part of the data you read, and they might not...

The only reliable way to read/write binary data is by keeping track of the length read from disk, and using that same length to write to the socket.

With that in mind, here's my thoughts on your code:

a) In C, the length variables that your API returns are all UNSIGNED fields (so they'd be data type U, not I as you have them coded). So your prototype SHOULD look like this: (Notice U instead of I)

D ifsReadByte PR 10u 0 ExtProc('_C_IFS_fread')
D inBuffer * Value
D inUnitSize 10u 0 Value
D inBufLen 10u 0 Value
D inFilePtr * Value

b) Your dow loop is discarding the length that fread() returns. It needs to be changed to save that length. This means that ifsReadByte() can't be coded on the DOW expression, it needs to be separate. You'll have to either do the reading at the top of the loop, or you'll have to code a "priming read". (Unless, of course, you want to wrap the whole thing into a subprocedure...)

For example:

C dou len < 1

C eval len = ifsReadByte( %addr(outBuffer)
C : 1
C : %size(outBuffer)
C : zp )

C if len >= 1
c callp(e) WriteDataToSocket( sockDescriptor
c : outBuffer
c : len )
c endif

C Enddo

c) As I described above, the length is very important when working with binary data. You'll see in the code above that I pass the 'len' variable to the WriteDataToSocket() routine... that means that routine will have to be modified to accept this extra parameter, and use it for the value it passes to the send() (or write() or whatever you've coded) API.


The parameter here that confuses me (mostly because I cannot find enough
good documentation on it that I can understand) is the inUnitSize. I
thought I understood what that meant, (i.e., Size in bytes of each element
to be read.), but it does not seem to make a difference what size I make
that, as long as it's not 1. So I'm thinking I don't really understand how
the inUnitSize and inBuflen work together.

It's for working with arrays of fixed-length data. Let's say you had an array like the following one:

D MyArray s 31p 4 dim(26)

Keep in mind, of course, that these routines are for C, and the C language exists everywhere, not just i5/OS. Most platforms don't have a built in database, and they save everything they do in stream files (like the ones we normally associate with the IFS).

To save the contents of the MyArray array to disk, you'd code something like this:

fwrite( %addr(MyArray): 16: 26: zp);

The 16 is because each packed number in the array is 16 bytes long (a 31p 4 field takes up 16 bytes of memory). The 26 is because there are 26 elements. The fwrite() API would return 26 to tell you that all 26 array elements were written.

Of course, that could would be better written if it used %size() and %elem() rather than hard-coding the 16 and 26, but I thought I'd code the actual numbers to make it a little clearer.

fwrite( %addr(MyArray): %size(MyArray): %elem(MyArray): zp);

(Note: when you use %size() on an array, and don't add *ALL, it only gives you the size of one element -- 16 in this example.)

When a program wanted to read back from disk, it'd do this:

count = fread( %addr(MyArray): %size(MyArray): %elem(MyArray): zp);

Now count = the number of array elements that were loaded from disk (26 if the entire array was loaded.).

If I were working with an array of data structures -- or multiple occurrence data structure -- then I could use fread() and fwrite() to read and write arrays of records -- making the support very similar to working with a program described, unkeyed, database file... and the count returned from fread() would be the number of records read... a useful tool on those platforms that don't have the built-in database.

Having the separate parameters for "size of an element" and "number of elements" is really just a convenience thing for the programmer. Under the covers, fread and fwrite really just multiply the two fields together to get a total number of bytes, and then they read or write that many bytes.

So in your case where you're just reading a buffer full of bytes, you want to set the size of each element (the inUnitSize parm in your prototype) to be 1, because the units you're reading are bytes -- and each byte is (duh) one byte long.

And you want to set the 'number of elements' parameter (inBufSize in your prototype) to be the total number of bytes you want to read or write on this call to fread/fwrite.

That's why, if you look at my re-design of your code (near the start of this message) you'll see I wrote it like this:

C eval len = ifsReadByte( %addr(outBuffer)
C : 1
C : %size(outBuffer)
C : zp )

The size of each element is 1, the number of elements is the %size() of your output buffer. So when it multiples %size(outBuffer) by 1, it'll come up with %size(outBuffer) and read that many bytes.

The API returns the number of elements (not the number of bytes) read. But, since (in this example) each element is one byte long, the number of bytes and the number of elements are the same thing. (In my array examples, above, they were not, however...)

If you reversed the parameters and coded it this way:

C eval len = ifsReadByte( %addr(outBuffer)
C : %size(outBuffer)
C : 1
C : zp )

This (incorrect, IMHO) version of the code tells the API that I only want to read one element, and it's 32767 bytes long. Again, the API just multiplies the fields, so the total bytes it reads would be correct. The problem is, the API will return either 0 or 1, since it returns the number of elements, and I only told it to read one element! This is not very useful unless you're working with data that's always fixed-length. With this code I can't tell how many bytes outBuffer is.

But coding it the other way (setting the size of each element to 1, and setting the number of elements to the numebr of bytes) the return value will be the number of actual bytes read.

C eval len = ifsReadByte( %addr(outBuffer)
C : 1
C : %size(outBuffer)
C : zp )

Hopefully that clarifies what those two fields are for, and what you'll want to use.



I'm not translating to EBCDIC and then back to ASCII or doing any
translation at all, by the way.


Again -- let me emphasize that the fopen() API will AUTOMATICALLY do this translation unless you tell it not to. Code the 'b' flag on fopen() to tell it you do not wish to have translation done.

As an Amazon Associate we earn from qualifying purchases.

This thread ...

Follow-Ups:
Replies:

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.