memory_io 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 23fd90a4ba0906c5264a4149f8992c1ca22ed251
4
+ data.tar.gz: d6414406dac7f68362f6d1aafb0d31966f8e21b0
5
+ SHA512:
6
+ metadata.gz: cc05ccbf5888b182138ff6112f8979e48875a8f3ab68730bf72e3cb5391e9205c32d7494e2d792196eda735eeac7e851e45fadcd54cdf51b9624d779b85e6492
7
+ data.tar.gz: b7ee99af0626e2486f8fab0da2b3c2bef47d877396a796118fc8ae8b4bfa456df609b0703d4da400b5a147e66a041ae557d69df2212b181f9d073353cf0e4bf6
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ [![Build Status](https://travis-ci.org/david942j/memory_io.svg?branch=master)](https://travis-ci.org/david942j/memory_io)
2
+ [![Gem Version](https://badge.fury.io/rb/memory_io.svg)](https://badge.fury.io/rb/memory_io)
3
+ [![Maintainability](https://api.codeclimate.com/v1/badges/dc8da34c5a8ab0095530/maintainability)](https://codeclimate.com/github/david942j/memory_io/maintainability)
4
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/dc8da34c5a8ab0095530/test_coverage)](https://codeclimate.com/github/david942j/memory_io/test_coverage)
5
+ [![Inline docs](https://inch-ci.org/github/david942j/memory_io.svg?branch=master)](https://inch-ci.org/github/david942j/memory_io)
6
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](http://choosealicense.com/licenses/mit/)
7
+
8
+ # Memory IO
9
+
10
+ Read/Write complicated structures in memory easily.
11
+
12
+ ## Installation
13
+
14
+ Available on RubyGems.org!
15
+
16
+ ```bash
17
+ $ gem install memory_io
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ### Read Process's Memory
23
+ ```
24
+ require 'memory_io'
25
+
26
+ process = MemoryIO.attach(`pidof victim`.to_i)
27
+ puts process.read('heap', 4, as: :u64).map { |c| '0x%016x' % c }
28
+ # 0x0000000000000000
29
+ # 0x0000000000000021
30
+ # 0x00000000deadbeef
31
+ # 0x0000000000000000
32
+ #=> nil
33
+
34
+ process.read('heap+0x10', 4, as: :u8).map { |c| '0x%x' % c }
35
+ #=> ['0xef', '0xbe', '0xad', '0xde']
36
+
37
+ process.read('libc', 4)
38
+ #=> "\x7fELF"
39
+ ```
@@ -0,0 +1,126 @@
1
+ require 'memory_io/types/types'
2
+
3
+ module MemoryIO
4
+ # Main class to use {MemoryIO}.
5
+ class IO
6
+ attr_reader :stream # @return [#pos=, #read, #write]
7
+
8
+ # Instantiate an {IO} object.
9
+ #
10
+ # @param [#pos=, #read, #write] stream
11
+ # The file-like object to be read/written.
12
+ # +file+ can be unwritable if you will not invoke any write-related method.
13
+ #
14
+ # If +stream.read(*)+ returns empty string or +nil+, it would be seen as reaching EOF.
15
+ def initialize(stream)
16
+ @stream = stream
17
+ end
18
+
19
+ # Read and convert result into custom type/stucture.
20
+ #
21
+ # @param [Integer] num_elements
22
+ # Number of elements to be read.
23
+ # This parameter must be positive and larger than zero.
24
+ #
25
+ # This parameter may effect the return type,
26
+ # see documents of return value.
27
+ # @param [Integer?] from
28
+ # Invoke +stream.pos = from+ before starting to read.
29
+ # +nil+ for not changing current position of stream.
30
+ # @param [nil, Symbol, MemoryIO::Types, Proc] as
31
+ # Decide the type/structure when reading.
32
+ # See {MemoryIO::Types} for all supported types.
33
+ #
34
+ # A +Proc+ is allowed, which should accept +stream+ as the first argument.
35
+ # The return value of the proc would be the return objects of this method.
36
+ #
37
+ # If +nil+ is given, this method returns a string and has same behavior as +::IO#read+.
38
+ # @param [Boolean] force_array
39
+ # When +num_elements+ equals to 1, the read +Object+ would be returned.
40
+ # Pass +true+ to this parameter to force this method returning an array.
41
+ #
42
+ # @return [String, Object, Array<Object>]
43
+ # There're multiple possible return types,
44
+ # which dependes on the value of parameter +num_elements+, +as+, and +force_array+.
45
+ #
46
+ # See examples for clear usage. The rule of return type is listed as following:
47
+ #
48
+ # * +as = nil+:
49
+ # A +String+ with length +num_elements+ is returned.
50
+ # * +as != nil+ and +num_elements = 1+ and +force_array = false+:
51
+ # An +Object+ is returned. The type of +Object+ depends on parameter +as+.
52
+ # * +as != nil+ and +num_elements = 1+ and +force_array = true+:
53
+ # An array with one element is returned.
54
+ # * +as != nil+ and +num_elements > 1+:
55
+ # An array with length +num_elements+ is returned.
56
+ #
57
+ # If EOF is occured, object(s) read will be returned.
58
+ #
59
+ # @example
60
+ # stream = StringIO.new('A' * 8 + 'B' * 8)
61
+ # io = MemoryIO.new(stream)
62
+ # io.read(9)
63
+ # #=> "AAAAAAAAB"
64
+ # io.read(100)
65
+ # #=> "BBBBBBB"
66
+ #
67
+ # # read two unsigned 32-bit integers starts from posistion 4
68
+ # io.read(2, from: 4, as: :u32)
69
+ # #=> [1094795585, 1111638594] # [0x41414141, 0x42424242]
70
+ #
71
+ # io.read(1, as: :u16)
72
+ # #=> 16962 # 0x4242
73
+ # io.read(1, as: :u16, force_array: true)
74
+ # #=> [16962]
75
+ # @example
76
+ # stream = StringIO.new("\xef\xbe\xad\xde")
77
+ # io = MemoryIO.new(stream)
78
+ # io.read(1, as: :u32)
79
+ # #=> 3735928559
80
+ # io.rewind
81
+ # io.read(1, as: :s32)
82
+ # #=> -559038737
83
+ # @example
84
+ # stream = StringIO.new("123\x0045678\x00")
85
+ # io = MemoryIO.new(stream)
86
+ # io.read(2, as: :c_str)
87
+ # #=> ["123", "45678"]
88
+ # @example
89
+ # # pass lambda to `as`
90
+ # stream = StringIO.new("\x03123\x044567")
91
+ # io = MemoryIO.new(stream)
92
+ # io.read(2, as: lambda { |stream| stream.read(stream.read(1).ord) })
93
+ # #=> ["123", "4567"]
94
+ #
95
+ # @note
96
+ # This method's arguments and return value are different with +::IO#read+.
97
+ # Check documents and examples.
98
+ def read(num_elements, from: nil, as: nil, force_array: false)
99
+ stream.pos = from if from
100
+ return stream.read(num_elements) if as.nil?
101
+ conv = to_proc(as, :read)
102
+ # TODO: handle eof
103
+ ret = Array.new(num_elements) { conv.call(stream) }
104
+ ret = ret.first if num_elements == 1 && !force_array
105
+ ret
106
+ end
107
+
108
+ # Set +stream+ to the beginning.
109
+ # i.e. invoke +stream.pos = 0+.
110
+ #
111
+ # @return [0]
112
+ def rewind
113
+ stream.pos = 0
114
+ end
115
+
116
+ private
117
+
118
+ def to_proc(as, rw)
119
+ ret = as.respond_to?(:call) ? as : MemoryIO::Types.get_proc(as, rw)
120
+ raise ArgumentError, <<-EOERR.strip unless ret.respond_to?(:call)
121
+ Invalid argument `as`: #{as.inspect}. It should be either a Proc or a supported type of MemoryIO::Types.
122
+ EOERR
123
+ ret
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,113 @@
1
+ module MemoryIO
2
+ # Records information of a process.
3
+ class Process
4
+ # @api private
5
+ # @return [#readable?, #writable?]
6
+ attr_reader :perm
7
+
8
+ # @api private
9
+ #
10
+ # Create process object from pid.
11
+ #
12
+ # @param [Integer] pid
13
+ # Process id.
14
+ #
15
+ # @note
16
+ # This class only supports procfs-based system. i.e. /proc is mounted and readable.
17
+ #
18
+ # @todo
19
+ # Support MacOS
20
+ # @todo
21
+ # Support Windows
22
+ def initialize(pid)
23
+ @pid = pid
24
+ @mem = "/proc/#{pid}/mem"
25
+ # check permission of '/proc/pid/mem'
26
+ @perm = MemoryIO::Util.file_permission(@mem)
27
+ # TODO: raise custom exception
28
+ raise Errno::ENOENT, @mem if perm.nil?
29
+ # FIXME: use logger
30
+ warn(<<-EOS.strip) unless perm.readable? || perm.writable?
31
+ You have no permission to read/write this process.
32
+
33
+ Check the setting of /proc/sys/kernel/yama/ptrace_scope, or try
34
+ again as the root user.
35
+
36
+ To enable attach another process, do:
37
+
38
+ $ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
39
+ EOS
40
+ end
41
+
42
+ # Parse +/proc/[pid]/maps+ to get all bases.
43
+ #
44
+ # @return [{Symbol => Integer}]
45
+ # Hash of bases.
46
+ #
47
+ # @example
48
+ # process = Process.new(`pidof victim`.to_i)
49
+ # puts process.bases.map { |key, val| format('%s: 0x%016x', key, val) }
50
+ # # vsyscall: 0xffffffffff600000
51
+ # # vdso: 0x00007ffd5b565000
52
+ # # vvar: 0x00007ffd5b563000
53
+ # # stack: 0x00007ffd5ad21000
54
+ # # ld: 0x00007f339a69b000
55
+ # # libc: 0x00007f33996f1000
56
+ # # heap: 0x00005571994a1000
57
+ # # victim: 0x0000557198bcb000
58
+ # #=> nil
59
+ def bases
60
+ file = "/proc/#{@pid}/maps"
61
+ stat = MemoryIO::Util.file_permission(file)
62
+ return {} unless stat && stat.readable?
63
+ maps = ::IO.binread(file).split("\n").map do |line|
64
+ # 7f76515cf000-7f76515da000 r-xp 00000000 fd:01 29360257 /lib/x86_64-linux-gnu/libnss_files-2.24.so
65
+ addr, _perm, _offset, _dev, _inode, pathname = line.strip.split(' ', 6)
66
+ next nil if pathname.nil?
67
+ addr = addr.to_i(16)
68
+ pathname = pathname[1..-2] if pathname =~ /^\[.+\]$/
69
+ pathname = ::File.basename(pathname)
70
+ [MemoryIO::Util.trim_libname(pathname).to_sym, addr]
71
+ end
72
+ maps.compact.reverse.to_h
73
+ end
74
+
75
+ # Read from process's memory.
76
+ #
77
+ # This method has *almost* same arguements and return types as {IO#read}.
78
+ # The only difference is this method needs parameter +addr+ (which
79
+ # will be passed to paramter +from+ in {IO#reada}).
80
+ #
81
+ # @param [Integer, String] addr
82
+ # The address start to read.
83
+ # When String is given, it will be safe-evaluated.
84
+ # You can use variables such as +'heap'/'stack'/'libc'+ in this parameter.
85
+ # See examples.
86
+ # @param [Integer] num_elements
87
+ # Number of elements to read. See {IO#read}.
88
+ #
89
+ # @return [String, Object, Array<Object>]
90
+ # See {IO#read}.
91
+ #
92
+ # @example
93
+ # process = MemoryIO.attach(`pidof victim`.to_i)
94
+ # puts process.read('heap', 4, as: :u64).map { |c| '0x%016x' % c }
95
+ # # 0x0000000000000000
96
+ # # 0x0000000000000021
97
+ # # 0x00000000deadbeef
98
+ # # 0x0000000000000000
99
+ # #=> nil
100
+ # process.read('heap+0x10', 4, as: :u8).map { |c| '0x%x' % c }
101
+ # #=> ['0xef', '0xbe', '0xad', '0xde']
102
+ #
103
+ # process.read('libc', 4)
104
+ # #=> "\x7fELF"
105
+ # @see IO#read
106
+ def read(addr, num_elements, **options)
107
+ addr = MemoryIO::Util.safe_eval(addr, bases)
108
+ File.open(@mem, 'rb') do |f|
109
+ MemoryIO::IO.new(f).read(num_elements, from: addr, **options)
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: ascii-8bit
2
+
3
+ require 'memory_io/types/type'
4
+
5
+ module MemoryIO
6
+ module Types
7
+ # Read a null-terminated string.
8
+ class CStr < Types::Type
9
+ # @api private
10
+ #
11
+ # @return [String]
12
+ # String without null byte.
13
+ def self.read(stream)
14
+ ret = ''
15
+ loop do
16
+ c = stream.read(1)
17
+ break if c.nil? || c == '' || c == "\x00"
18
+ ret << c
19
+ end
20
+ ret
21
+ end
22
+
23
+ # @api private
24
+ #
25
+ # @param [String] val
26
+ # A null byte would be appended if +val+ not ends with null byte.
27
+ def self.write(stream, val)
28
+ val = val.to_s
29
+ val << "\x00" unless val.end_with?("\x00")
30
+ stream.write(val)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,60 @@
1
+ require 'memory_io/types/type'
2
+
3
+ module MemoryIO
4
+ module Types
5
+ # @api private
6
+ # Register numbers to {Types}.
7
+ #
8
+ # All types registerd by this class are assumed as *little endian*.
9
+ class Number
10
+ # @param [Integer] bytes
11
+ # Bytes.
12
+ # @param [Boolean] signed
13
+ # Signed or unsigned.
14
+ # @param [String] pack_str
15
+ # The indicator to be passed to +Array#pack+ and +String#unpack+.
16
+ def initialize(bytes, signed, pack_str)
17
+ @bytes = bytes
18
+ @signed = signed
19
+ @pack_str = pack_str
20
+ end
21
+
22
+ # @return [Integer]
23
+ def read(stream)
24
+ unpack(stream.read(@bytes))
25
+ end
26
+
27
+ # @param [Integer] val
28
+ def write(stream, val)
29
+ stream.write(pack(val))
30
+ end
31
+
32
+ private
33
+
34
+ def unpack(str)
35
+ val = str.unpack(@pack_str).first
36
+ val -= (2**@bytes) if @signed && val >= (2**(@bytes - 1))
37
+ val
38
+ end
39
+
40
+ def pack(val)
41
+ [val].pack(@pack_str)
42
+ end
43
+
44
+ # Register (un)signed n-bites integers.
45
+ {
46
+ 8 => 'C',
47
+ 16 => 'S',
48
+ 32 => 'I',
49
+ 64 => 'Q'
50
+ }.each do |t, c|
51
+ Type.register("s#{t}".to_sym, Number.new(t, true, c))
52
+ Type.register("u#{t}".to_sym, Number.new(t, false, c))
53
+ end
54
+
55
+ # Register floating numbers.
56
+ Type.register(:float, Number.new(4, false, 'F'))
57
+ Type.register(:double, Number.new(8, false, 'D'))
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,37 @@
1
+ module MemoryIO
2
+ module Types
3
+ # The base class, all descendents of this class would be consider as a valid 'type'.
4
+ class Type
5
+ class << self
6
+ # Find the subclass of {Type} by symbol.
7
+ #
8
+ # @param [Symbol] symbol
9
+ # Symbol to be find.
10
+ #
11
+ # @return [#read, #write]
12
+ # The object that registered in {.register}.
13
+ def find(symbol)
14
+ @map[symbol]
15
+ end
16
+
17
+ # @api private
18
+ #
19
+ # @param [Symbol] symbol
20
+ # Symbol name that used for searching.
21
+ # @param [#read, #write] klass
22
+ # Normally, +klass+ is a descendent of {Type}.
23
+ #
24
+ # @return [void]
25
+ def register(symbol, klass)
26
+ @map ||= {}
27
+ @map[symbol] = klass
28
+ end
29
+
30
+ # TO record descendants.
31
+ def inherited(klass)
32
+ register(MemoryIO::Util.underscore(klass.name).to_sym, klass)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,56 @@
1
+ require 'memory_io/types/type'
2
+ require 'memory_io/util'
3
+
4
+ require 'memory_io/types/c_str'
5
+ require 'memory_io/types/number'
6
+
7
+ module MemoryIO
8
+ # Module that includes multiple types.
9
+ #
10
+ # Supported types are all descendants of {Types::Type}.
11
+ module Types
12
+ module_function
13
+
14
+ # @api private
15
+ #
16
+ # Returns the class whose name matches +name+.
17
+ #
18
+ # This method would search all descendants of {Types::Type}.
19
+ #
20
+ # @return [Symbol] name
21
+ # Class name to be searched.
22
+ #
23
+ # @return [Class?]
24
+ # The class.
25
+ #
26
+ # @example
27
+ # Types.find(:u64)
28
+ # #=> MemoryIO::Types::U64
29
+ #
30
+ # Types.find(:c_str)
31
+ # #=> MemoryIO::Types::CStr
32
+ def find(name)
33
+ Types::Type.find(Util.underscore(name.to_s).to_sym)
34
+ end
35
+
36
+ # Returns a callable object according to +name+.
37
+ #
38
+ # @param [Symbol] name
39
+ # The name of type.
40
+ # @param [:read, :write] rw
41
+ # Read or write?
42
+ #
43
+ # @return [Proc?]
44
+ # The proc that accepts +stream+ as the first parameter.
45
+ #
46
+ # @example
47
+ # Types.get_proc(:c_str, :write)
48
+ # #=> #<Method: MemoryIO::Types::CStr.write>
49
+ # Types.get_proc(:s32, :read)
50
+ # #=> #<Method: MemoryIO::Types::Number#read>
51
+ def get_proc(name, rw)
52
+ klass = find(name)
53
+ klass && klass.method(rw)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,101 @@
1
+ require 'ostruct'
2
+ require 'dentaku'
3
+
4
+ module MemoryIO
5
+ # Defines utility methods.
6
+ module Util
7
+ module_function
8
+
9
+ # Convert input into snake-case.
10
+ #
11
+ # This method also removes strings before +'::'+ in +str+.
12
+ #
13
+ # @param [String] str
14
+ # String to be converted.
15
+ #
16
+ # @return [String]
17
+ # Converted string.
18
+ #
19
+ # @example
20
+ # Util.underscore('MemoryIO')
21
+ # #=> 'memory_io'
22
+ #
23
+ # Util.underscore('MyModule::MyClass')
24
+ # #=> 'my_class'
25
+ def underscore(str)
26
+ return '' if str.empty?
27
+ str = str.split('::').last
28
+ str.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
29
+ str.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
30
+ str.downcase!
31
+ str
32
+ end
33
+
34
+ # @api private
35
+ #
36
+ # @param [String] file
37
+ # File name.
38
+ #
39
+ # @return [#readable?, #writable?, nil]
40
+ # Struct with two boolean method.
41
+ # +nil+ for file not exists or is inaccessible.
42
+ def file_permission(file)
43
+ return nil unless File.file?(file)
44
+ stat = File.stat(file)
45
+ # we do a trick here because /proc/[pid]/mem might be marked as readable but fails at sysopen.
46
+ os = OpenStruct.new(readable?: stat.readable_real?, writable?: stat.writable_real?)
47
+ begin
48
+ os.readable? && File.open(file, 'rb').close
49
+ rescue Errno::EACCES
50
+ os[:readable?] = false
51
+ end
52
+ begin
53
+ os.writable? && File.open(file, 'wb').close
54
+ rescue Errno::EACCES
55
+ os[:writable?] = false
56
+ end
57
+ os
58
+ end
59
+
60
+ # Evaluate string safely.
61
+ #
62
+ # @param [String] str
63
+ # String to be evaluated.
64
+ # @param [{Symbol => Integer}] vars
65
+ # Predefined variables
66
+ #
67
+ # @return [Integer]
68
+ # Result.
69
+ #
70
+ # @example
71
+ # Util.safe_eval('heap + 0x10 * pp', heap: 0xde00, pp: 8)
72
+ # #=> 56960 # 0xde80
73
+ def safe_eval(str, **vars)
74
+ return str if str.is_a?(Integer)
75
+ # dentaku 2 doesn't support hex
76
+ str = str.gsub(/0x[0-9a-zA-Z]+/) { |c| c.to_i(16) }
77
+ Dentaku::Calculator.new.store(vars).evaluate(str)
78
+ end
79
+
80
+ # Remove extension name (.so) and version in library name.
81
+ #
82
+ # @param [String] name
83
+ # Original library filename.
84
+ #
85
+ # @return [String]
86
+ # Name without version and '.so'.
87
+ #
88
+ # @example
89
+ # Util.trim_libname('libc-2.24.so')
90
+ # #=> 'libc'
91
+ # Util.trim_libname('libcrypto.so.1.0.0')
92
+ # #=> 'libcrypto'
93
+ # Util.trim_libname('not_a_so')
94
+ # #=> 'not_a_so'
95
+ def trim_libname(name)
96
+ type1 = '(-[\d.]+)?\.so$'
97
+ type2 = '\.so.\d+[\d.]+$'
98
+ name.sub(/#{type1}|#{type2}/, '')
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,4 @@
1
+ module MemoryIO
2
+ # Current gem version.
3
+ VERSION = '0.0.0'.freeze
4
+ end
data/lib/memory_io.rb ADDED
@@ -0,0 +1,21 @@
1
+ # MemoryIO - Read/Write structures in memory.
2
+ #
3
+ # @author david942j
4
+ module MemoryIO
5
+ module_function
6
+
7
+ # Get a process by process id.
8
+ #
9
+ # @param [Integer] pid
10
+ # Process Id in linux.
11
+ #
12
+ # @return [MemoryIO::Process]
13
+ # A process object for further usage.
14
+ def attach(pid)
15
+ MemoryIO::Process.new(pid)
16
+ end
17
+ end
18
+
19
+ require 'memory_io/io'
20
+ require 'memory_io/process'
21
+ require 'memory_io/version'
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: memory_io
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - david942j
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-01-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dentaku
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.11
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 2.0.11
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: codeclimate-test-reporter
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.6'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.6'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '12.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '12.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rspec
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '3.5'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.5'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rubocop
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.52'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.52'
89
+ - !ruby/object:Gem::Dependency
90
+ name: simplecov
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: 0.13.0
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: 0.13.0
103
+ - !ruby/object:Gem::Dependency
104
+ name: yard
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0.9'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.9'
117
+ description: ''
118
+ email:
119
+ - david942j@gmail.com
120
+ executables: []
121
+ extensions: []
122
+ extra_rdoc_files: []
123
+ files:
124
+ - README.md
125
+ - lib/memory_io.rb
126
+ - lib/memory_io/io.rb
127
+ - lib/memory_io/process.rb
128
+ - lib/memory_io/types/c_str.rb
129
+ - lib/memory_io/types/number.rb
130
+ - lib/memory_io/types/type.rb
131
+ - lib/memory_io/types/types.rb
132
+ - lib/memory_io/util.rb
133
+ - lib/memory_io/version.rb
134
+ homepage: https://github.com/david942j/memory_io
135
+ licenses:
136
+ - MIT
137
+ metadata: {}
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: 2.1.0
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 2.6.14
155
+ signing_key:
156
+ specification_version: 4
157
+ summary: memory_io
158
+ test_files: []