class MemoryIO::IO

Main class to use {MemoryIO}.

Attributes

stream[R]

Public Class Methods

new(stream) click to toggle source

Instantiate an {IO} object.

@param [#pos, pos=, read, write] stream

The file-like object to be read/written.
+file+ can be un-writable if you will not invoke any write-related method.

If +stream.read(*)+ returns empty string or +nil+, it would be seen as reaching EOF.
# File lib/memory_io/io.rb, line 18
def initialize(stream)
  @stream = stream
end

Public Instance Methods

read(num_elements, from: nil, as: nil, force_array: false) click to toggle source

Read and convert result into custom type/structure.

@param [Integer] num_elements

Number of elements to be read.
This parameter must be positive and larger than zero.

This parameter may effect the return type,
see documents of return value.

@param [Integer?] from

Invoke +stream.pos = from+ before starting to read.
+nil+ for not changing current position of stream.

@param [nil, Symbol, Proc] as

Decide the type/structure when reading.
See {MemoryIO::Types} for all supported types.

A +Proc+ is allowed, which should accept +stream+ as the first argument.
The return value of the proc would be the return objects of this method.

If +nil+ is given, this method returns a string and has same behavior as +::IO#read+.

@param [Boolean] force_array

When +num_elements+ equals to 1, the read +Object+ would be returned.
Pass +true+ to this parameter to force this method returning an array.

@return [String, Object, Array<Object>]

There're multiple possible return types,
which depends on the value of parameter +num_elements+, +as+, and +force_array+.

See examples for clear usage. The rule of return type is listed as following:

* +as = nil+:
  A +String+ with length +num_elements+ is returned.
* +as != nil+ and +num_elements = 1+ and +force_array = false+:
  An +Object+ is returned. The type of +Object+ depends on parameter +as+.
* +as != nil+ and +num_elements = 1+ and +force_array = true+:
  An array with one element is returned.
* +as != nil+ and +num_elements > 1+:
  An array with length +num_elements+ is returned.

If EOF is occured, object(s) read will be returned.

@example

stream = StringIO.new('A' * 8 + 'B' * 8)
io = MemoryIO::IO.new(stream)
io.read(9)
#=> "AAAAAAAAB"
io.read(100)
#=> "BBBBBBB"

# read two unsigned 32-bit integers starts from posistion 4
io.read(2, from: 4, as: :u32)
#=> [1094795585, 1111638594] # [0x41414141, 0x42424242]

io.read(1, as: :u16)
#=> 16962 # 0x4242
io.read(1, as: :u16, force_array: true)
#=> [16962]

@example

stream = StringIO.new("\xef\xbe\xad\xde")
io = MemoryIO::IO.new(stream)
io.read(1, as: :u32)
#=> 3735928559
io.rewind
io.read(1, as: :s32)
#=> -559038737

@example

stream = StringIO.new("123\x0045678\x00")
io = MemoryIO::IO.new(stream)
io.read(2, as: :c_str)
#=> ["123", "45678"]

@example

# pass lambda to `as`
stream = StringIO.new("\x03123\x044567")
io = MemoryIO::IO.new(stream)
io.read(2, as: lambda { |stream| stream.read(stream.read(1).ord) })
#=> ["123", "4567"]

@note

This method's arguments and return value are different with +::IO#read+.
Check documents and examples.

@see Types

# File lib/memory_io/io.rb, line 103
def read(num_elements, from: nil, as: nil, force_array: false)
  stream.pos = from if from
  return stream.read(num_elements) if as.nil?

  conv = to_proc(as, :read)
  # TODO: handle eof
  ret = Array.new(num_elements) { conv.call(stream) }
  ret = ret.first if num_elements == 1 && !force_array
  ret
end
rewind() click to toggle source

Set stream to the beginning. i.e. invoke +stream.pos = 0+.

@return [0]

# File lib/memory_io/io.rb, line 177
def rewind
  stream.pos = 0
end
write(objects, from: nil, as: nil) click to toggle source

Write to stream.

@param [Object, Array<Object>] objects

Objects to be written.

@param [Integer] from

The position to start to write.

@param [nil, Symbol, Proc] as

Decide the method to process writing procedure.
See {MemoryIO::Types} for all supported types.

A +Proc+ is allowed, which should accept +stream+ and one object as arguments.

If +objects+ is a descendant instance of {Types::Type} and +as+ is +nil,
+objects.class+ will be used for +as+.
Otherwise, when +as = nil+, this method will simply call +stream.write(objects)+.

@return [void]

@example

stream = StringIO.new
io = MemoryIO::IO.new(stream)
io.write('abcd')
stream.string
#=> "abcd"

io.write([1, 2, 3, 4], from: 2, as: :u16)
stream.string
#=> "ab\x01\x00\x02\x00\x03\x00\x04\x00"

io.write(['A', 'BB', 'CCC'], from: 0, as: :c_str)
stream.string
#=> "A\x00BB\x00CCC\x00\x00"

@example

stream = StringIO.new
io = MemoryIO::IO.new(stream)
io.write(%w[123 4567], as: ->(s, str) { s.write(str.size.chr + str) })
stream.string
#=> "\x03123\x044567"

@example

stream = StringIO.new
io = MemoryIO::IO.new(stream)
cpp_string = CPP::String.new('A' * 4, 15, 16)
# equivalent to io.write(cpp_string, as: :'cpp/string')
io.write(cpp_string)
stream.string
#=> "\x10\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00AAAA\x00"

@see Types

# File lib/memory_io/io.rb, line 164
def write(objects, from: nil, as: nil)
  stream.pos = from if from
  as ||= objects.class if objects.class.ancestors.include?(MemoryIO::Types::Type)
  return stream.write(objects) if as.nil?

  conv = to_proc(as, :write)
  Array(objects).map { |o| conv.call(stream, o) }
end

Private Instance Methods

to_proc(as, rw) click to toggle source

@api private

# File lib/memory_io/io.rb, line 184
    def to_proc(as, rw)
      ret = as.respond_to?(rw) ? as.method(rw) : as
      ret = ret.respond_to?(:call) ? ret : MemoryIO::Types.get_proc(ret, rw)
      raise ArgumentError, <<-EOERR.strip unless ret.respond_to?(:call)

Invalid argument `as`: #{as.inspect}. It should be either a Proc or a supported type of MemoryIO::Types.
      EOERR

      ret
    end