Next: , Previous: , Up: Streams  


6.10.2 Your Own Stream

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 position:. You can see what will be read next with peek, and you can read the next element with next.

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 can use next:put:, like this:

   s := ReadWriteStream on: (Array new: 0)
   s next: 4 put: 'Hi!'
   s position: 1
   s do: [:x | x printNl]

Next: , Previous: , Up: Streams