Class: MemoryIO::IO

Inherits:
Object
  • Object
show all
Defined in:
lib/memory_io/io.rb

Overview

Main class to use MemoryIO.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stream, endian: :native, pointer_size: MemoryIO::Context::DEFAULT_POINTER_SIZE) ⇒ IO

Instantiate an MemoryIO::IO object.

Examples:

# a dump captured on a 32-bit big endian machine
MemoryIO::IO.new(File.open('core.dump', 'rb'), endian: :big, pointer_size: 4)

Parameters:

  • stream (#pos, #pos=, #read, #write)

    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.

  • endian (:little, :big, :native) (defaults to: :native)

    Byte order of the memory reached through stream. The default is right whenever that memory belongs to a process on this host, and should be given when it does not, such as a dump taken elsewhere.

  • pointer_size (Integer) (defaults to: MemoryIO::Context::DEFAULT_POINTER_SIZE)

    Size of a pointer in that memory, in bytes.



34
35
36
37
38
# File 'lib/memory_io/io.rb', line 34

def initialize(stream, endian: :native, pointer_size: MemoryIO::Context::DEFAULT_POINTER_SIZE)
  @stream = stream
  @context = MemoryIO::Context.new(endian: endian, pointer_size: pointer_size)
  @tagged = MemoryIO::Stream.new(stream, @context)
end

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context.



15
# File 'lib/memory_io/io.rb', line 15

attr_reader :stream, :context

#stream#pos, ... (readonly)

Returns The stream given at instantiation.

Returns:

  • (#pos, #pos=, #read, #write)

    The stream given at instantiation.



15
16
17
# File 'lib/memory_io/io.rb', line 15

def stream
  @stream
end

Instance Method Details

#read(num_elements, from: nil, as: nil, force_array: false) ⇒ String, ...

Note:

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

Read and convert result into custom type/structure.

Examples:

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 starting from position 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]
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
stream = StringIO.new("123\x0045678\x00")
io = MemoryIO::IO.new(stream)
io.read(2, as: :c_str)
#=> ["123", "45678"]
# reading beyond the end of stream returns what was read
stream = StringIO.new("\x01\x02\x03\x04")
io = MemoryIO::IO.new(stream)
io.read(3, as: :u32)
#=> [67305985]

# an object that can't be read in full is not returned
io.rewind
io.read(1, as: :u64)
#=> nil
# 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"]

Parameters:

  • num_elements (Integer)

    Number of elements to be read. Zero reads nothing, as it does for ::IO#read.

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

  • from (Integer?) (defaults to: nil)

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

  • as (nil, Symbol, Proc) (defaults to: nil)

    Decide the type/structure when reading. See 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.

  • force_array (Boolean) (defaults to: false)

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

Returns:

  • (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 occurred, only the objects that could be read in full are returned, so the result may be shorter than num_elements (possibly empty).

Raises:

  • (ArgumentError)

    num_elements is negative or is not an Integer.

See Also:



136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/memory_io/io.rb', line 136

def read(num_elements, from: nil, as: nil, force_array: false)
  unless num_elements.is_a?(Integer) && !num_elements.negative?
    raise ArgumentError, "num_elements must be a non-negative Integer, got #{num_elements.inspect}"
  end

  stream.pos = from if from
  return stream.read(num_elements) if as.nil?

  conv = to_proc(as, :read)
  ret = read_elements(num_elements, conv)
  ret = ret.first if num_elements == 1 && !force_array
  ret
end

#rewind0

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

Returns:

  • (0)


213
214
215
# File 'lib/memory_io/io.rb', line 213

def rewind
  stream.pos = 0
end

#write(objects, from: nil, as: nil) ⇒ void

This method returns an undefined value.

Write to stream.

Examples:

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"
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"
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"

Parameters:

  • objects (Object, Array<Object>)

    Objects to be written.

  • from (Integer) (defaults to: nil)

    The position to start to write.

  • as (nil, Symbol, Proc) (defaults to: nil)

    Decide the method to process writing procedure. See 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).

See Also:



200
201
202
203
204
205
206
207
# File 'lib/memory_io/io.rb', line 200

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(@tagged, o) }
end