ReadWriteStream Issues

Richard A. O'Keefe ok at cs.otago.ac.nz
Tue May 6 02:18:20 UTC 2003


Time for a look at the ANSI standard.  (Not for a full and final legal
settlement, but for some guidance, is all.)

(Part of) the protocol hierarchy looks like this,
using a Javaish syntax (because protocols aren't classes,
and because it's hard to draw a DAG in ASCII):

protocol "sequenced stream"
    close                // dissociate stream from backing store
    contents             // return complete contents of stream.
    isEmpty              // self contents isEmpty, NOT at end
    position             // return current logical position
    position: n          // set logical position
    reset                // same as self position: 0
    setToEnd             // same as self position: (self contents size)

    (I must say that having position/position: in the Stream class in
    Squeak is a *major* pain.  Nearly every stream I've ever written
    has been unable to inherit from Stream because of that.  I also
    note that #contents is unimplementable for streams connnected to
    many kinds of devices.)

protocol "collection stream" extends "sequenced stream"
    contents            // returns same class as aColl select: [...]
                        // where aColl is underlying collection; the
                        // answer might or might not be aColl itself.

protocol "gettable stream"
    atEnd		// at end of readable data yet?
    do: aBlock          // [self atEnd] whileFalse: [aBlock value: self next]
    next                // return next item (undefined when atEnd)
    next: n             // return collection of next n items
    nextLine            // return next line
    nextMatchFor: x	// return self next = x
    peek                // return next item but don't move over it, nil atEnd
    peekFor: x          // return self atEnd not and: [self peek = x]
    skip: n             // skip next n items, stop early atEnd
    skipTo: x           // [self atEnd] whileFalse: [self next = x ifTrue:
                        // [^true]]. ^false
    upTo: x             // collect items; stop collecting just before x;
                        // stop reading just after x or AtEnd.

    (#nextLine is clearly the odd man out here.  What does
     (ReadStream on: #(1 2 3 4)) nextLine mean?)

protocol ReadStream extends "gettable stream", "collection stream"
    next: n             // returns same class as self contents
    upTo: x             // returns same class as self contents

protocol "puttable stream"
    cr			// self nextPut: Character cr.
    flush		// "update backing store".
    nextPut: x          // write x.  If position is at end, append, else
                        // replace & step over next element.
    nextPutAll: xs	// xs do: [:x | self nextPut: x]
    space		// self nextPut: Character space.
    tab                 // self nextPut: Character tab.

protocol WriteStream extends "puttable stream", "collection stream"
    // no extra methods, no changes to semantics.

protocol ReadWriteStream extends ReadStream, WriteStream
    // no extra methods, no changes to semantics.

factory ReadStream
    on: aCollection	// return a ReadStream reading from aCollection

    Past sequence values: none.
    Future sequence values: the elements of aCollection.
    In effect, the position is 0 and the read limit is aCollection size.

factory WriteStream
    with: aCollection	// return a WriteStream writing to aCollection

    Past sequence values: the elements of aCollection.
    Future sequnce values: none.
    In effect, the position and the write limit are both aCollection size.

factory ReadWriteStream
    with: aCollection   // return a ReadWriteStream based on aCollection

    Past sequence values: the elements of aCollection.
    Future sequnce values: none.
    In effect, the position, read limit, and write limit are all
    aCollection size.

What we _don't_ find is a definition for ReadWriteStream>>on:

Let's look at Squeak 2.8.

Stream
  PositionableStream
    ReadStream
    WriteStream
      ReadWriteStream

PositionableStream class>>on: aCollection
    ^self basicNew on: aCollection
PositionableStream class>>on: aCollection from: first to: last
    "You were maybe expecting something clever, maybe?"
    ^self basicNew on: (aCollection copyFrom: first to: last)

PositionableStream>>on: aCollection
    collection := aCollection.
    readLimit := aCollection size.
    position := 0.
    self reset "position := 0 again."

WriteStream class>>on: aCollection
    "inherited from PositionableStream"
WriteStream class>>on: aCollection from: first to: last
    ^self basicNew on: aCollection from: first to: last
WriteStream class>>with: aCollection
    ^self basicNew with: aCollection
WriteStream class>>with: aCollection from: first to: last
    ^self basicNew with: (aCollection copyFrom: first to: last)

WriteStream>>on: aCollection "boils down to"
    collection := aCollection.
    position := 0.
    readLimit := 0. "OOPS"
    writeLimit := aCollection size.
WriteStream>>on: aCollection from: first to: last "boils down to"
    collection := aCollection.
    readLimit := writeLimit := aCollection size min: last.
    position := first - 1 max: 0.
WriteStream>>with: aCollection "boils down to"
    collection := aCollection.
    position := readLimit := writeLimit := aCollection size.

We see a strange thing here.
    WriteStream on: aColl
sets readLimit to 0, but
    WriteStream on: aColl from: 1 to: aColl size
sets readLimit to aColl size.

ReadWriteStream class
    "inherits #on:, #on:from:to:, #with:, #with:from:to:"
ReadWriteStream
    "inherits #on:, #on:from:to:, #with:"

It looks very much as if WriteStream>>on: aCollection
should read 
    self on: aCollection from: 1 to: aCollection size.

This doesn't seem to be a new problem; I was looking at the code in 2.8.



More information about the Squeak-dev mailing list