[Scummvm-devel] Major "bug" / misfeature in the Stream system
Max Horn
max at quendi.de
Sun Sep 7 23:03:02 CEST 2008
Hi folks,
just a brief note to all: We discovered recently that our Stream
system (including class File, the MemoryStreams, etc.) has a problem
in that it never was clearly specified how the eos() (end-of-stream)
and ioFailed() methods should work; in particular, when exactly they
should return true/false, and when those values get reset.
Turns out that File works similiar to how C's stdio works: The EOF/EOS
flag only gets set after you tried reading data *beyond* the end of
the file. That is, after reading the last byte of the file, EOS is not
signaled; only if you then try to read on.
In contrast to that, MemoryReadStream, SubReadStream etc. would signal
EOS immediately once the last byte was read.
So far, this didn't really cause issues, but it became very apparent
when I recently worked on our SeekableReadStream::readLine code, which
relies on the precise behavior of eos()...
In fact, lots of our code doesn't ever check for eos() -- rather, it
checkes for ioFailed exclusively. Which works for File objects, since
those return true in ioFailed at EOF, too -- which MemoryReadStream
does *not*, it just always returns false in ioFailed. Ooops...
Anyway, this is a bad thing for both people who want to write Stream
objects as well as for those who want to use them. So, I think we need
to do something about this. Here's what I propose:
1) Decide what shall be considered "correct" behavior from now on. My
proposal: Let's do it exactly as stdio does. The advantage is that in
stdio, the behavior of ferror/feof is thoroughly defined and well-
understood by people. That is: EOS is only signaled after trying to
read *beyond* the EOS (so File is "correct", but MemoryStream etc.
need to introduce an internal EOS flag).
Also, add a new method err() / ioErr() or so, which is modelled after
ferror.
Keep the current ioFailed() as an alias for "eos() || ioErr()" to ease
transition of existing code.
Add a new clearIoStatus() (or so) method which resets both the error
and the EOS flag (like clearerr() in stdio); the current clearIoFailed
will just call through to that.
An alternative idea would be to model after C++'s iostream; but I am
not sure that would benefit us.
2) Write unit tests for all stream classes to verify the "correct"
behavior (even if the code does not behave correctly yet ;)
3) Fix / enhance all Stream classes as needed
4) Go through all client code (mostly engines) to verify they still
work fine. try to replace ioFailed where possible, so we can get rid
of it.
Does that sounds like a reasonable plan? Any volunteers to help with
it? Because I am pretty swamped with other stuff :).
Cheers,
Max
More information about the Scummvm-devel
mailing list