iyi

Standard library, 0.18.0

std/io

std/io: byte streams beyond the standard ones.

import std/io to use it. Source: src/std/io.iyi at v0.18.0.

The prelude's IyiIO reads and writes a descriptor. This module adds to it, on top of its public read_bytes/write only: read_byte, write_byte, read_char, lines, each_line, << and IyiIO.copy. Positioning (seek, pos, rewind) is not offered on a descriptor stream: the prelude has no seek intrinsic and its read buffer would make a kernel offset a lie.

Memory is an IyiIO over a byte buffer in memory: it grows as it is written, reads from a cursor that pos=, rewind and clear move, and to_s gives back everything written. Anything that takes an IyiIO takes a Memory.

Sized reads at most a given number of bytes from another IyiIO; Delimited reads up to (and consumes) a byte delimiter, leaving the inner stream just past it; MultiWriter writes to several IyiIOs at once. All three are IyiIOs themselves: a reader refuses writes, the writer refuses reads, each with a sentence.

Hexdump prints bytes sixteen to a line, as hexdump -C does: an eight-digit hex offset, the bytes in two groups of eight, the printable ASCII between bars.

ByteFormat::LittleEndian / BigEndian encode and decode Int32, Int64, UInt64 and UInt8 into bytes, into a pointer or an IyiIO, through unsigned byte arithmetic: every value, negative or not, round-trips. SystemEndian follows the target; NetworkEndian is big-endian.

Refused: a negative count or limit, a position past the buffer, reading a closed stream, writing a reader, reading a writer, malformed UTF-8 in read_char, and a decode that runs out of bytes.

ByteFormatstruct

std/ioiyi doc src/std/io.iyi
pub struct ByteFormat
  struct BigEndian
    def self.decode_int32(buffer : Pointer(UInt8)) : Int32
    def self.decode_int32(io : IyiIO) : Int32
    def self.decode_int64(buffer : Pointer(UInt8)) : Int64
    def self.decode_int64(io : IyiIO) : Int64
    def self.decode_uint64(buffer : Pointer(UInt8)) : UInt64
    def self.decode_uint64(io : IyiIO) : UInt64
    def self.decode_uint8(buffer : Pointer(UInt8)) : UInt8
    def self.decode_uint8(io : IyiIO) : UInt8
    def self.encode(value : Int32, buffer : Pointer(UInt8)) : Nil
    def self.encode(value : Int64, buffer : Pointer(UInt8)) : Nil
    def self.encode(value : UInt64, buffer : Pointer(UInt8)) : Nil
    def self.encode(value : UInt8, buffer : Pointer(UInt8)) : Nil
    def self.encode(value : Int32, io : IyiIO) : Nil
    def self.encode(value : Int64, io : IyiIO) : Nil
    def self.encode(value : UInt64, io : IyiIO) : Nil
    def self.encode(value : UInt8, io : IyiIO) : Nil
    def initialize
  end
  # Two's complement by arithmetic, so a negative value never meets an
  # unsigned conversion: the bit pattern of a signed integer as a UInt64
  # and back.
  struct Bits
    def self.get_big(buffer : Pointer(UInt8), count : Int32) : UInt64
    def self.get_little(buffer : Pointer(UInt8), count : Int32) : UInt64
    def initialize
    def self.of_int32(value : Int32) : UInt64
    def self.of_int64(value : Int64) : UInt64
    def self.put_big(bits : UInt64, buffer : Pointer(UInt8), count : Int32) : Nil
    # The low `count` bytes of `bits`, least significant first.
    def self.put_little(bits : UInt64, buffer : Pointer(UInt8), count : Int32) : Nil
    # `count` bytes out of `io`, or a refusal naming the shortfall.
    def self.take(io : IyiIO, count : Int32) : String
    def self.to_int32(bits : UInt64) : Int32
    def self.to_int64(bits : UInt64) : Int64
  end
  struct LittleEndian
    def self.decode_int32(buffer : Pointer(UInt8)) : Int32
    def self.decode_int32(io : IyiIO) : Int32
    def self.decode_int64(buffer : Pointer(UInt8)) : Int64
    def self.decode_int64(io : IyiIO) : Int64
    def self.decode_uint64(buffer : Pointer(UInt8)) : UInt64
    def self.decode_uint64(io : IyiIO) : UInt64
    def self.decode_uint8(buffer : Pointer(UInt8)) : UInt8
    def self.decode_uint8(io : IyiIO) : UInt8
    def self.encode(value : Int32, buffer : Pointer(UInt8)) : Nil
    def self.encode(value : Int64, buffer : Pointer(UInt8)) : Nil
    def self.encode(value : UInt64, buffer : Pointer(UInt8)) : Nil
    def self.encode(value : UInt8, buffer : Pointer(UInt8)) : Nil
    def self.encode(value : Int32, io : IyiIO) : Nil
    def self.encode(value : Int64, io : IyiIO) : Nil
    def self.encode(value : UInt64, io : IyiIO) : Nil
    def self.encode(value : UInt8, io : IyiIO) : Nil
    def initialize
  end
  alias NetworkEndian = BigEndian
  alias SystemEndian = LittleEndian
  def initialize
end

Delimitedclass

Another stream up to a delimiter. The delimiter is consumed and not answered; the inner stream is left just past it. Without a delimiter the whole rest of the stream is answered.

std/ioiyi doc src/std/io.iyi
pub class Delimited < Reader
  def delimiter : String
  # Exact, as the prelude's is: the inner stream is read until a content
  # byte is held or the delimiter (or the end) is met. Answering from the
  # inner stream alone said "not yet" with nothing but the delimiter left,
  # and the next read answered nil.
  def eof? : Bool
  def initialize(io : IyiIO, delimiter : String)
  def read_byte : UInt8 | ::Nil
  def write(bytes : Pointer(UInt8), count : Int32) : Nil
end

Hexdumpclass

Bytes printed sixteen to a line, hexdump -C style, on an IyiIO.

std/ioiyi doc src/std/io.iyi
pub class Hexdump
  def dump(bytes : Pointer(UInt8), count : Int32) : Nil
  def dump(text : String) : Nil
  # Everything left in `source`.
  def dump(source : IyiIO) : Nil
  def initialize(out __arg0 : IyiIO)
end

Memoryclass

An IyiIO over bytes in memory. Writes land at the cursor and grow the buffer; reads advance the cursor; to_s is the whole buffer.

std/ioiyi doc src/std/io.iyi
pub class Memory < ::IyiIO
  # Forgets every byte; the cursor returns to the start.
  def clear : Nil
  def close : Nil
  def closed? : Bool
  def empty? : Bool
  def eof? : Bool
  def flush : Nil
  def initialize(capacity : Int32 = 64)
  # A buffer holding a copy of `text`'s bytes, the cursor at its start.
  def initialize(text : String)
  # The cursor, in bytes from the start. Int64, as a file's position is:
  # with `std/file` imported every `IyiIO` has a `pos : Int64`, and a
  # narrower answer here made `pos` on any `IyiIO` fail to compile.
  def pos : Int64
  def pos=(position : Int32) : Nil
  # The cursor moved through an `IyiIO`: the stream's own position, not
  # the descriptor's that `std/file` would move.
  def pos=(position : Int64) : Int64
  def read_all : String
  def read_byte : UInt8 | ::Nil
  def read_bytes(count : Int32) : String
  def read_line(chomp : Bool = false) : String | ::Nil
  def rewind : Nil
  # Bytes held, written or given.
  def size : Int32
  def to_s : String
  def write(bytes : Pointer(UInt8), count : Int32) : Nil
end

MultiWriterclass

One stream written to several. close closes this writer, not the streams behind it.

std/ioiyi doc src/std/io.iyi
pub class MultiWriter < ::IyiIO
  def close : Nil
  def closed? : Bool
  def eof? : Bool
  def flush : Nil
  def initialize(writers : Array(IyiIO))
  def read_all : String
  def read_byte : UInt8 | ::Nil
  def read_bytes(count : Int32) : String
  def read_line(chomp : Bool = false) : String | ::Nil
  def write(bytes : Pointer(UInt8), count : Int32) : Nil
end

PipeReaderclass

The reading end of IyiIO.pipe.

std/ioiyi doc src/std/io.iyi
pub class PipeReader < ::IyiIO
  def initialize(channel : Channel(String))
end

PipeWriterclass

The writing end of IyiIO.pipe: every write is sent at once.

std/ioiyi doc src/std/io.iyi
pub class PipeWriter < ::IyiIO
  def initialize(channel : Channel(String))
end

Sizedclass

At most limit bytes of another stream.

std/ioiyi doc src/std/io.iyi
pub class Sized < Reader
  def eof? : Bool
  def initialize(io : IyiIO, limit : Int32)
  def read_all : String
  def read_byte : UInt8 | ::Nil
  # The inner stream is asked for at most 64 KB at a time: a descriptor
  # stream's `read_bytes` allocates the whole count before it reads, and
  # `read_all` under a limit of Int32::MAX took 2 GB to read two bytes.
  def read_bytes(count : Int32) : String
  # Bytes the limit still allows, whether or not the stream has them.
  def remaining : Int32
  def write(bytes : Pointer(UInt8), count : Int32) : Nil
end

IyiIOclass, reopened

std/ioiyi doc src/std/io.iyi
class ::IyiIO
  def <<(value) : self
  # Everything left in `source` (at most `limit` bytes) written to
  # `target`, which is flushed; answers the number of bytes moved.
  def self.copy(source : IyiIO, target : IyiIO, limit : Int32 | ::Nil = nil) : Int64
  def each_line(chomp : Bool = false, &block : (String -> Nil)) : Nil
  # Every remaining line, `chomp` dropping the line ends.
  def lines(chomp : Bool = false) : Array(String)
  # The two ends of a pipe between tasks: what is written to the writer is
  # read from the reader, in order.
  #
  #     reader, writer = IyiIO.pipe
  #     group do |g|
  #       g.spawn { IyiIO.copy(file, writer); writer.close; 0 }
  #       g.spawn { IyiIO.copy(reader, socket.io); 0 }
  #     end
  #
  # Each write is delivered at once, as one piece, and a write waits while
  # *capacity* pieces are unread; a read waits for one. Closing the writer
  # ends the reader after the last piece; closing the reader refuses the
  # next write, as a pipe whose reader is gone does. The pipe is its
  # thread's, for the tasks on the thread that made it.
  def self.pipe(capacity : Int32 = 16) : ::Tuple(Std::Io::PipeReader, Std::Io::PipeWriter)
  # The next byte, or nil at the end.
  def read_byte : UInt8 | ::Nil
  # The next UTF-8 code point, or nil at the end. A lead byte that is not
  # one (a continuation byte, C0/C1, F5 and above), a continuation byte that
  # is not one, or a sequence the stream ends inside is refused. The first
  # continuation byte is held to the range its lead allows (Unicode Table
  # 3-7), so an overlong form, a surrogate and a code point past U+10FFFF
  # are refused at the byte that gives them away, not decoded into a Char
  # outside Unicode.
  def read_char : Char | ::Nil
  def write_byte(byte : UInt8) : Nil
end