deprecating std.stream, std.cstream, std.socketstream

Artur Skawina art.08.09 at gmail.com
Wed May 16 13:38:54 PDT 2012


On 05/16/12 21:38, H. S. Teoh wrote:
> On Wed, May 16, 2012 at 12:48:49PM -0500, Andrei Alexandrescu wrote:
>> On 5/16/12 12:34 PM, Steven Schveighoffer wrote:
>>> In other words, ranges aren't enough.
>>
>> This is copiously clear to me, but the way I like to think about it
>> is by extending the notion of range (with notions such as e.g.
>> BufferedRange, LookaheadRange, and such) instead of developing an
>> abstraction independent from ranges and then working on stitching
>> that with ranges.
> [...]
> 
> One direction that _could_ be helpful, perhaps, is to extend the concept
> of range to include, let's tentatively call it, a ChunkedRange.
> Basically a ChunkedRange implements the usual InputRange operations
> (empty, front, popfront) but adds the following new primitives:
> 
> - bool hasAtLeast(R)(R range, int n) - true if underlying range has at
>   least n elements left;
> 
> - E[] frontN(R)(R range, int n) - returns a slice containing the front n
>   elements from the range: this will buffer the next n elements from the
>   range if they aren't already; repeated calls will just return the
>   buffer;
> 
> - void popN(R)(R range, int n) - discards the first n elements from the
>   buffer, thus causing the next call to frontN() to fetch more data if
>   necessary.
> 
> These are all tentative names, of course. But the idea is that you can
> keep N elements of the range "in view" at a time, without having to
> individually read them out and save them in a second buffer, and you can
> advance this view once you're done with the current data and want to
> move on.
> 
> Existing range operations like popFrontN, take, takeExactly, drop, etc.,
> can be extended to take advantage of the extra functionality of
> ChunkedRanges. (Perhaps popFrontN can even be merged with popN, since
> they amount to the same thing.)
> 
> Using a ChunkedRange allows you to write functions that parse a
> particular range and return a range of chunks (say, a deserializer that
> returns a range of objects given a range of bytes).
> 
> Thinking on it a bit further, perhaps we can call this a WindowedRange,
> since it somewhat resembles the sliding window protocol where you keep a
> "window" of sequential packet ids in an active buffer, and remove them
> from the buffer as they get ack'ed (consumed by popN). The buffer thus
> acts like a "window" into the next n elements in the range, which can be
> "slid forward" as data is consumed.

Right now, everybody reinvents this, with a slightly different interface...
It's really obvious, needed and just has to be standardized.

A few notes:

hasAtLeast is redundant as it can be better expressed as .length; what would
be the point of wrapping 'r.length>=n'? An '.available' property would be
useful to find eg out how much can be consumed w/o blocking, but that one 
should return a size_t too.

'E[] frontN' should have a version that returns all available elements; i 
called it '@property E[] fronts()' here. It's more efficient that way and
doesn't rely on the compiler to inline and optimize the limit checks away.

PopN -- well, its signature here is 'void popFronts(size_t n)', other than
that, there's no difference.

Similar things are necessary for output ranges. Here, what i needed was:

   void put(ref E el)
   void puts(E[] els)
   @property size_t free() // Not the most intuitive name w/o context;
                           // returns the number of E's that can be 'put()'
                           // w/o blocking.                            

Note that all of this doesn't address the consume-variable-sized-chunks issue.
But that can now be efficiently handled by another layer on top.


On 05/16/12 22:15, Steven Schveighoffer wrote:
> I still don't get the need to "add" this to ranges.  The streaming API works fine on its own.

This is not an argument against a streaming API (at least not for me), but
for efficient ranges. With the API above I can shift tens of gigabytes of
data per second between threads. And still use the 'std' range API and
everything that works with it...

> But there is an omission with your proposed API regardless -- reading data is a mutating event.  It destructively mutates the underlying data stream so that you cannot get the data again.  This means you must double-buffer data in order to support frontN and popN that are not necessary with a simple read API.
> 
> For example:
> 
> auto buf = new ubyte[1000000];
> stream.read(buf);
> 
> does not need to first buffer the data inside the stream and then copy it to buf, it can read it from the OS *directly* into buf.

Sometimes having the buffer managed by 'stream' and 'read()' returning a slice
into it works (this is what 'fronts' above does). Reusing a caller managed
buffer can be useful in other cases, yes. 

artur


More information about the Digitalmars-d mailing list