On Thu, 14 Oct 2010 01:32:49 +0400, Steven Schveighoffer <schvei...@yahoo.com> wrote:

Before responding directly, I'll say I think this is on the right track. IMO, buffering should be transparent when it can be, meaning you should be able to have control over the buffering. The abstraction should look like this:

specific application -> buffered streams -> OS abstraction -> Low level calls.

Denis' proposal covers up to OS abstraction. What we need on top of that is a buffer layer.

On Wed, 13 Oct 2010 12:16:38 -0400, Denis Koroskin <2kor...@gmail.com> wrote:

I'll explain my I/O streams implementation below in case you didn't read my message (I've changed some stuff a little since then). My Stream interface is very simple:

// A generic stream
interface Stream
{
     @property InputStream input();
     @property OutputStream output();
     @property SeekableStream seekable();
     @property bool endOfStream();
     void close();
}

You may ask, why separate Input and Output streams? Well, that's because you either read from them, write from them, or both. Some streams are read-only (think Stdin), some write-only (Stdout), some support both, like FileStream. Right?

I feel we can possibly make this a compile-time decision. Can we do something like this:

interface Stream : InputStream, OutputStream {}

Not exactly. Does FileStream support writing when you open file for reading? Does it support reading when you open for writing? So, you may or may not read from a generic stream, and you also may or may not write to a generic stream. With a design like that you can make a mistake: if a stream isn't readable, you have no reference to invoke read() method on.

Essentially, it's near zero the times that I decide at runtime whether I'm opening a file for reading, writing or both. So why not build that into the type, and then we have the compiler to tell us when something can't be used for reading or writing?

Similarly, a stream is either seekable, or not. SeekableStreams allow stream cursor manipulation:

interface SeekableStream : Stream
{
     long getPosition(Anchor whence = Anchor.begin);
     void setPosition(long position, Anchor whence = Anchor.begin);
}

A seekable interface is one of those things that's really hard to get right. In Tango, we eventually got rid of the seekable interface and just added seek methods to all the low level stream interfaces. The rationale is that most of the time seekability is not a requirement you can set when opening a file. You open a file for reading or writing, but not for seeking. So it's almost necessary that seekability is a runtime decision (because the OS decides it outside of your control).

There are, of course, streams that will not be seekable (netowork sockets), but you just throw an exception when seeking such a stream. The only thing I'd create is a way to determine seekability without throwing an exception (i.e. a canSeek property). But most of the time you know whether a stream is seekable without having to check.


Essentially, there is no difference between bool canSeek and SeekableStream seekable(). However, it's almost always a good idea to check whether a stream actually supports seeking before doing so, and seekable() statically enforces you to do so.


InputStream doesn't really has many methods:

interface InputStream
{
        // reads up to buffer.length bytes from a stream
        // returns number of bytes read
        // throws on error
        size_t read(ubyte[] buffer);

        // reads from current position
        AsyncReadRequest readAsync(ubyte[] buffer, Mailbox* mailbox = null);
}

I'd say void[] is better here, since you aren't creating the buffer, you're accepting it. Using ubyte makes for awkward casts when you are reading binary data into specific structures.

ditto for OutputStream.

-Steve


--
Using Opera's revolutionary email client: http://www.opera.com/mail/

Reply via email to