Unlike a pipe you might create in C, the underlying storage of a Stream is under your control. Thus, a Stream can provide an anonymous buffer of data, but it can also provide a stream-like interpretation to an existing array of data. Consider this example:
a := Array new: 10 a at: 4 put: 1234 a at: 9 put: 5678 s := ReadWriteStream on: a. s inspect s position: 1 s inspect s nextPut: 11; nextPut: 22 (a at: 1) printNl a do: [:x| x printNl] s position: 2 s do: [:x| x printNl] s position: 5 s do: [:x| x printNl] s inspect
The key is the
on: message; it tells a stream class to
create itself in terms of the existing storage. Because of
polymorphism, the object specified by on: does not have to
be an Array; any object which responds to numeric at: messages
can be used. If you happen to have the NiledArray
class still loaded from the previous chapter, you might try
streaming over that kind of array instead.
You're wondering if you're stuck with having to know how much data will be queued in a Stream at the time you create the stream. If you use the right class of stream, the answer is no. A ReadStream provides read-only access to an existing collection. You will receive an error if you try to write to it. If you try to read off the end of the stream, you will also get an error.
By contrast, WriteStream and ReadWriteStream (used in our example) will tell the underlying collection to grow when you write off the end of the existing collection. Thus, if you want to write several strings, and don't want to add up their lengths yourself:
s := ReadWriteStream on: String new s inspect s nextPutAll: 'Hello, ' s inspect s nextPutAll: 'world' s inspect s position: 1 s inspect s do: [:c | stdout nextPut: c ] s contents
In this case, we have used a String as the collection
for the Stream. The
printOn: messages add bytes to the initially
empty string. Once we've added the data, you can
continue to treat the data as a stream. Alternatively, you
can ask the stream to return to you the underlying object.
After that, you can use the object (a String, in this example)
using its own access methods.
There are many amenities available on a stream object.
You can ask if there's more to read with
atEnd. You can
query the position with
position, and set it with
You can see what will be read next with
you can read the next element with
In the writing direction, you can write an element with
nextPut:. You don't need to worry about objects doing a
printOn: with your stream as a destination; this operation
ends up as a sequence of
nextPut: operations to your stream.
If you have a collection of things to write, you can use
nextPutAll: with the collection as an argument; each member
of the collection will be written onto the stream. If you
want to write an object to the stream several times, you
next:put:, like this:
s := ReadWriteStream on: (Array new: 0) s next: 4 put: 'Hi!' s position: 1 s do: [:x | x printNl]