module IO::Like

IO::Like is a module which provides most of the basic input and output functions of IO objects using methods named unbuffered_read, unbuffered_write, and unbuffered_seek.

The definition of this particular module is equivalent to whatever version-specific module provides the closest interface to the IO implementation of the Ruby interpreter running this library. For example, IO::Like will be equivalent to IO::Like_1_8_6 under Ruby version 1.8.6 while it will be equivalent to IO::Like_1_8_7 under Ruby version 1.8.7.

When considering any of the IO::Like modules for use in a class, the following documentation holds true.

Readers

In order to use this module to provide input methods, a class which includes it must provide the unbuffered_read method which takes one argument, a length, as follows:

def unbuffered_read(length)
  ...
end

This method must return at most length bytes as a String, raise EOFError if reading begins at the end of data, and raise SystemCallError on error. Errno::EAGAIN should be raised if there is no data to return immediately and the read operation should not block. Errno::EINTR should be raised if the read operation is interrupted before any data is read.

In 1.9 the returned string is expected to have 'binary' encoding.

Writers

In order to use this module to provide output methods, a class which includes it must provide the unbuffered_write method which takes a single string argument as follows:

def unbuffered_write(string)
  ...
end

In 1.9 the supplied string argument will have 'binary' encoding.

This method must either return the number of bytes written to the stream, which may be less than the length of string in bytes, OR must raise an instance of SystemCallError. Errno::EAGAIN should be raised if no data can be written immediately and the write operation should not block. Errno::EINTR should be raised if the write operation is interrupted before any data is written.

Seekers

In order to use this module to provide seeking methods, a class which includes it must provide the unbuffered_seek method which takes two required arguments, an offset and a start position, as follows:

def unbuffered_seek(offset, whence)
  ...
end

This method must return the new position within the data stream relative to the beginning of the stream and should raise SystemCallError on error. offset can be any integer and whence can be any of IO::SEEK_SET, IO::SEEK_CUR, or IO::SEEK_END. They are interpreted together as follows:

      whence | resulting position
-------------+------------------------------------------------------------
IO::SEEK_SET | Add offset to the position of the beginning of the stream.
-------------+------------------------------------------------------------
IO::SEEK_CUR | Add offset to the current position of the stream.
-------------+------------------------------------------------------------
IO::SEEK_END | Add offset to the position of the end of the stream.

Duplexed Streams

In order to create a duplexed stream where writing and reading happen independently of each other, override the duplexed? method to return true and then provide the unbuffered_read and unbuffered_write methods. Do NOT provide an unbuffered_seek method or the contents of the internal read and write buffers may be lost unexpectedly.


NOTE: Due to limitations of Ruby's finalizer, IO::Like#close is not automatically called when the object is garbage collected, so it must be explicitly called when the object is no longer needed or risk losing whatever data remains in the internal write buffer.

Non-blocking Streams

As above unbuffered_read and unbuffered_write should raise Errno::EAGAIN if they should not block.

The read_nonblock and write_nonblock methods of IO::Like will attempt to call nonblock=(true) on the underlying stream before calling unbuffered_read or unbuffered_write. This is the equivalent of Ruby setting the O_NONBLOCK flag on traditional file descriptor based IO.

The default implementation of nonblock= raises Errno::EBADF. For streams where unbuffered_read is always non blocking this can be overridden with a no-op.

Nonblocking streams should also provide a more optimal implementation of read_ready? and write_ready? which by default simply calls Kernel.sleep(1) and is called by the blocking read and write methods in response to Errno::EAGAIN.