[fpc-pascal] TStream.ReadBuffer doesn't try hard enough to read the requested bytes
Michael Van Canneyt
michael at freepascal.org
Sun Jun 16 18:46:49 CEST 2013
On Sun, 16 Jun 2013, Michalis Kamburelis wrote:
> 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);
> if Read(Buffer,Count)<Count then
> Raise EReadError.Create(SReadError);
> 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.
The above implementation should not be changed, it is Delphi compatible:
TStream.ReadBuffer works on the assumption that Read will always return
the amount of bytes requested if they are available.
So, if you want to make changes, there is no reason why TStream.Read should not benefit
from 'trying harder', it could try harder to actually get the requested number of
bytes from the underlying low-level implementation. Some TStream descendents already
work that way (compression, hash, encoding), others don't.
This in turn means that affected TStream descendents such as TFileStream or THandleStream
should try harder to read the requested number of bytes, taking into account the
specifics of the underlying file/socket/whatever protocol.
More information about the fpc-pascal