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



Scott:
fread() function seems to be designed to read stream file where data in
the file is segmented and, just like record in a database file, each
segment has the same length. Of course, by passing those parameters the
way you suggested would allow Shannon to know the length of data that
this api has read. I'm just curious as why not just use the read()
function. This function seems to be a more suitable function to use
when needing to read an image file.





-----Original Message-----
From: rpg400-l-bounces@xxxxxxxxxxxx
[mailto:rpg400-l-bounces@xxxxxxxxxxxx] On Behalf Of Scott Klement
Sent: Thursday, November 29, 2007 7:08 PM
To: RPG programming on the AS400 / iSeries
Subject: Re: Send_File API versus Send API over a Socket


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.