[fpc-pascal] TStream.ReadBuffer doesn't try hard enough to read the requested bytes
Michalis Kamburelis
michalis.kambi at gmail.com
Sun Jun 16 01:08:19 CEST 2013
Hi,
I'm often reading fixed-size structures using TStream.ReadBuffer,
working under the assumption that it will fail only if it's not possible
to read the requested amount of bytes (that is, if the stream ended
prematurely). This is actually the documented usage, see
http://www.freepascal.org/docs-html/rtl/classes/tstream.readbuffer.html
and see many usage examples of ReadBuffer in FPC, Lazarus etc. sources.
I'm using various stream classes in my code (TFileStream, TGZFileStream,
TMemoryStream, TBase64DecodingStream...) and I always depend that
ReadBuffer will work correctly for all streams.
But in some hardly-reproducible cases sometimes ReadBuffer fails even
though I know I have more bytes. Looking at the sources, I was surprised
to see this ReadBuffer implementation (in rtl/objpas/classes/streams.inc ):
procedure TStream.ReadBuffer(var Buffer; Count: Longint);
begin
if Read(Buffer,Count)<Count then
Raise EReadError.Create(SReadError);
end;
This is quite incorrect, IMHO. The TStream.Read implementations of
various streams can sometimes return less Count than requested, and
that's Ok, and that doesn't mean that stream ended. Stream ended only
when TStream.Read returns exactly zero.
Which means that ReadBuffer should retry calling Read, in a loop, trying
to finish the reading. ReadBuffer should raise an exception only when
Read returns zero (before we can gather all the Count bytes).
Right now, the way I see it, a lot of the code using ReadBuffer works
purely by accident. At least if it's supposed to work with any TStream
descendant.
For example: on Linux, TFileStream.Read calls fpRead which is a system
call, and it definitely can return less than requested amount of bytes,
and it doesn't mean that stream ended. (See e.g. Libc docs:
http://www.gnu.org/software/libc/manual/html_mono/libc.html#I_002fO-Primitives
: "If read returns at least one character, there is no way you can tell
whether end-of-file was reached. But if you did reach the end, the next
read will return zero. ") So if you use TFileStream with ReadBuffer,
your code is working purely by accident right now...
If you agree, I can provide a patch to ReadBuffer (and similar to
WriteBuffer).
Michalis
More information about the fpc-pascal
mailing list