memory_io 0.2.0 → 1.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 +4 -4
- data/LICENSE +21 -0
- data/README.md +38 -12
- data/lib/memory_io/context.rb +95 -0
- data/lib/memory_io/error.rb +38 -0
- data/lib/memory_io/io.rb +83 -11
- data/lib/memory_io/logger.rb +32 -0
- data/lib/memory_io/process.rb +76 -15
- data/lib/memory_io/stream.rb +54 -0
- data/lib/memory_io/types/basic/number.rb +105 -23
- data/lib/memory_io/types/clang/c_str.rb +17 -3
- data/lib/memory_io/types/cpp/string.rb +21 -14
- data/lib/memory_io/types/record.rb +9 -7
- data/lib/memory_io/types/type.rb +38 -18
- data/lib/memory_io/types/types.rb +6 -6
- data/lib/memory_io/util.rb +79 -29
- data/lib/memory_io/version.rb +1 -1
- data/lib/memory_io.rb +27 -3
- metadata +66 -15
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'memory_io/context'
|
|
4
|
+
require 'memory_io/error'
|
|
3
5
|
require 'memory_io/types/type'
|
|
4
6
|
|
|
5
7
|
module MemoryIO
|
|
@@ -8,64 +10,144 @@ module MemoryIO
|
|
|
8
10
|
module Basic
|
|
9
11
|
# Register numbers to {Types}.
|
|
10
12
|
#
|
|
11
|
-
#
|
|
13
|
+
# The byte order is taken from the {MemoryIO::Context} of the stream being
|
|
14
|
+
# read, so the same type reads correctly from memory of either byte order.
|
|
12
15
|
#
|
|
13
|
-
# This class registered (un)signed
|
|
16
|
+
# This class registered (un)signed (8, 16, 32, 64)-bit integers and IEEE-754 floating numbers.
|
|
14
17
|
class Number
|
|
18
|
+
# Indicators of the integer widths, by size in bytes.
|
|
19
|
+
# A single byte has no byte order to indicate.
|
|
20
|
+
INTEGER_INDICATORS = { 1 => 'C', 2 => 'S', 4 => 'L', 8 => 'Q' }.freeze
|
|
21
|
+
|
|
22
|
+
# Indicators of the real widths, by size in bytes and then byte order.
|
|
23
|
+
#
|
|
24
|
+
# @example
|
|
25
|
+
# # 'e' and 'E' are IEEE-754 single and double precision, little endian.
|
|
26
|
+
REAL_INDICATORS = { 4 => { little: 'e', big: 'g' }, 8 => { little: 'E', big: 'G' } }.freeze
|
|
27
|
+
|
|
28
|
+
# Appended to an integer indicator to fix its byte order.
|
|
29
|
+
ENDIAN_INDICATORS = { little: '<', big: '>' }.freeze
|
|
30
|
+
|
|
15
31
|
# @param [Integer] bytes
|
|
16
32
|
# Bytes.
|
|
17
33
|
# @param [Boolean] signed
|
|
18
34
|
# Signed or unsigned.
|
|
19
|
-
# @param [
|
|
20
|
-
#
|
|
21
|
-
def initialize(bytes, signed,
|
|
35
|
+
# @param [Boolean] real
|
|
36
|
+
# Whether this type holds a real number rather than an integer.
|
|
37
|
+
def initialize(bytes, signed, real: false)
|
|
22
38
|
@bytes = bytes
|
|
23
39
|
@signed = signed
|
|
24
|
-
@
|
|
40
|
+
@real = real
|
|
41
|
+
@range = value_range unless real
|
|
42
|
+
@indicators = ENDIAN_INDICATORS.keys.to_h { |e| [e, indicator(e)] }
|
|
25
43
|
end
|
|
26
44
|
|
|
27
45
|
# @return [Integer]
|
|
46
|
+
#
|
|
47
|
+
# @raise [EOFError]
|
|
48
|
+
# Fewer than +bytes+ bytes remain in +stream+.
|
|
28
49
|
def read(stream)
|
|
29
|
-
|
|
50
|
+
endian = MemoryIO::Context.of(stream).endian
|
|
51
|
+
unpack(MemoryIO::Util.read_exactly(stream, @bytes), endian)
|
|
30
52
|
end
|
|
31
53
|
|
|
32
54
|
# @param [Integer] val
|
|
55
|
+
#
|
|
56
|
+
# @raise [MemoryIO::ValueOutOfRangeError]
|
|
57
|
+
# +val+ doesn't fit in this type, which would otherwise be written truncated.
|
|
33
58
|
def write(stream, val)
|
|
34
|
-
|
|
59
|
+
endian = MemoryIO::Context.of(stream).endian
|
|
60
|
+
raise MemoryIO::ValueOutOfRangeError, out_of_range_message(val) if out_of_range?(val, endian)
|
|
61
|
+
|
|
62
|
+
stream.write(pack(val, endian))
|
|
35
63
|
end
|
|
36
64
|
|
|
37
65
|
private
|
|
38
66
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
67
|
+
# Anything that isn't a number of the matching kind is left for
|
|
68
|
+
# +Array#pack+ to reject.
|
|
69
|
+
#
|
|
70
|
+
# @return [Boolean]
|
|
71
|
+
def out_of_range?(val, endian)
|
|
72
|
+
if @range
|
|
73
|
+
val.is_a?(Integer) && !@range.cover?(val)
|
|
74
|
+
else
|
|
75
|
+
val.is_a?(Numeric) && overflows?(val, endian)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# A finite value that packs to an infinity has exceeded what the type
|
|
80
|
+
# can represent. Losing precision is inherent to the type and allowed,
|
|
81
|
+
# as is writing an infinity that was asked for.
|
|
82
|
+
#
|
|
83
|
+
# @return [Boolean]
|
|
84
|
+
def overflows?(val, endian)
|
|
85
|
+
val.infinite?.nil? && pack(val, endian).unpack1(@indicators[endian]).infinite?
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @return [Range]
|
|
89
|
+
def value_range
|
|
90
|
+
bits = @bytes * 8
|
|
91
|
+
@signed ? (-(2**(bits - 1))..((2**(bits - 1)) - 1)) : (0..((2**bits) - 1))
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# @return [String]
|
|
95
|
+
def out_of_range_message(val)
|
|
96
|
+
return format('%s exceeds the range of %d-bit floating number', val, @bytes * 8) unless @range
|
|
97
|
+
|
|
98
|
+
format('%s is out of range for %d-bit %s integer (%s..%s)',
|
|
99
|
+
hex(val), @bytes * 8, @signed ? 'signed' : 'unsigned',
|
|
100
|
+
hex(@range.first), hex(@range.last))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# +format+'s '%#x' renders a negative number in two's complement notation.
|
|
104
|
+
#
|
|
105
|
+
# @return [String]
|
|
106
|
+
#
|
|
107
|
+
# @example
|
|
108
|
+
# hex(-128)
|
|
109
|
+
# #=> '-0x80'
|
|
110
|
+
def hex(val)
|
|
111
|
+
format('%s0x%x', val.negative? ? '-' : '', val.abs)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# @return [String]
|
|
115
|
+
# The +Array#pack+ indicator of this type in +endian+ byte order.
|
|
116
|
+
def indicator(endian)
|
|
117
|
+
return REAL_INDICATORS[@bytes][endian] if @real
|
|
118
|
+
return INTEGER_INDICATORS[@bytes] if @bytes == 1
|
|
119
|
+
|
|
120
|
+
INTEGER_INDICATORS[@bytes] + ENDIAN_INDICATORS[endian]
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def unpack(str, endian)
|
|
124
|
+
val = str.unpack1(@indicators[endian])
|
|
125
|
+
# a real is already signed by its representation
|
|
126
|
+
return val if @real
|
|
127
|
+
|
|
128
|
+
val -= (2**(@bytes * 8)) if @signed && val >= (2**((@bytes * 8) - 1))
|
|
42
129
|
val
|
|
43
130
|
end
|
|
44
131
|
|
|
45
|
-
def pack(val)
|
|
46
|
-
[val].pack(@
|
|
132
|
+
def pack(val, endian)
|
|
133
|
+
[val].pack(@indicators[endian])
|
|
47
134
|
end
|
|
48
135
|
|
|
49
136
|
# Register (un)signed n-bits integers.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
16 => 'S',
|
|
53
|
-
32 => 'I',
|
|
54
|
-
64 => 'Q'
|
|
55
|
-
}.each do |t, c|
|
|
56
|
-
Type.register(Number.new(t / 8, true, c),
|
|
137
|
+
[8, 16, 32, 64].each do |t|
|
|
138
|
+
Type.register(Number.new(t / 8, true),
|
|
57
139
|
alias: [:"basic/s#{t}", :"s#{t}"],
|
|
58
140
|
doc: "A signed #{t}-bit integer.")
|
|
59
|
-
Type.register(Number.new(t / 8, false
|
|
141
|
+
Type.register(Number.new(t / 8, false),
|
|
60
142
|
alias: [:"basic/u#{t}", :"u#{t}"],
|
|
61
143
|
doc: "An unsigned #{t}-bit integer.")
|
|
62
144
|
end
|
|
63
145
|
|
|
64
146
|
# Register floating numbers.
|
|
65
|
-
Type.register(Number.new(4,
|
|
147
|
+
Type.register(Number.new(4, true, real: true),
|
|
66
148
|
alias: %i[basic/float float],
|
|
67
149
|
doc: 'IEEE-754 32-bit floating number.')
|
|
68
|
-
Type.register(Number.new(8,
|
|
150
|
+
Type.register(Number.new(8, true, real: true),
|
|
69
151
|
alias: %i[basic/double double],
|
|
70
152
|
doc: 'IEEE-754 64-bit floating number.')
|
|
71
153
|
end
|
|
@@ -12,17 +12,31 @@ module MemoryIO
|
|
|
12
12
|
# A null-terminated string.
|
|
13
13
|
class CStr < Types::Type
|
|
14
14
|
|
|
15
|
+
# Number of bytes to fetch at a time while searching for the terminator.
|
|
16
|
+
CHUNK_SIZE = 1024
|
|
17
|
+
|
|
15
18
|
# @api private
|
|
16
19
|
#
|
|
20
|
+
# The terminator is searched for in blocks rather than byte by byte,
|
|
21
|
+
# and +stream+ is left just after it so the next read starts at the
|
|
22
|
+
# following string.
|
|
23
|
+
#
|
|
17
24
|
# @return [String]
|
|
18
25
|
# String without null byte.
|
|
19
26
|
def self.read(stream)
|
|
20
27
|
ret = +''
|
|
21
28
|
loop do
|
|
22
|
-
|
|
23
|
-
break if
|
|
29
|
+
chunk = stream.read(CHUNK_SIZE)
|
|
30
|
+
break if chunk.nil? || chunk.empty?
|
|
31
|
+
|
|
32
|
+
terminator = chunk.index("\x00")
|
|
33
|
+
if terminator
|
|
34
|
+
ret << chunk[0, terminator]
|
|
35
|
+
stream.pos -= chunk.size - terminator - 1
|
|
36
|
+
break
|
|
37
|
+
end
|
|
24
38
|
|
|
25
|
-
ret <<
|
|
39
|
+
ret << chunk
|
|
26
40
|
end
|
|
27
41
|
ret
|
|
28
42
|
end
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'memory_io/context'
|
|
4
|
+
require 'memory_io/logger'
|
|
3
5
|
require 'memory_io/types/type'
|
|
6
|
+
require 'memory_io/util'
|
|
4
7
|
|
|
5
8
|
module MemoryIO
|
|
6
9
|
module Types
|
|
@@ -22,9 +25,7 @@ module MemoryIO
|
|
|
22
25
|
# std::string uses inlined-buffer if string length isn't larger than {LOCAL_CAPACITY}.
|
|
23
26
|
LOCAL_CAPACITY = 15
|
|
24
27
|
|
|
25
|
-
attr_reader :data # @return [::String]
|
|
26
|
-
attr_reader :capacity # @return [Integer]
|
|
27
|
-
attr_reader :dataplus # @return [Integer]
|
|
28
|
+
attr_reader :data, :capacity, :dataplus # @return [::String] # @return [Integer] # @return [Integer]
|
|
28
29
|
|
|
29
30
|
# Instantiate a {CPP::String} object.
|
|
30
31
|
#
|
|
@@ -33,6 +34,7 @@ module MemoryIO
|
|
|
33
34
|
# @param [Integer] dataplus
|
|
34
35
|
# A pointer.
|
|
35
36
|
def initialize(data, capacity, dataplus)
|
|
37
|
+
super()
|
|
36
38
|
@data = data
|
|
37
39
|
@capacity = capacity
|
|
38
40
|
@dataplus = dataplus
|
|
@@ -51,21 +53,20 @@ module MemoryIO
|
|
|
51
53
|
# @param [String] str
|
|
52
54
|
def data=(str)
|
|
53
55
|
@data = str
|
|
54
|
-
|
|
56
|
+
return unless str.size > capacity
|
|
57
|
+
|
|
58
|
+
MemoryIO.logger.warn("Length of str (#{str.size}) is larger than capacity (#{capacity})")
|
|
55
59
|
end
|
|
56
60
|
|
|
57
61
|
# Custom inspect view.
|
|
58
62
|
#
|
|
59
63
|
# @return [String]
|
|
60
|
-
#
|
|
61
|
-
# @todo
|
|
62
|
-
# Let it be colorful in pry.
|
|
63
64
|
def inspect
|
|
64
|
-
format(
|
|
65
|
+
format('#<%s @data=%s, @capacity=%d, @dataplus=0x%s>',
|
|
65
66
|
self.class.name,
|
|
66
67
|
data.inspect,
|
|
67
68
|
capacity,
|
|
68
|
-
dataplus)
|
|
69
|
+
dataplus.to_s(16).rjust(SIZE_T * 2, '0'))
|
|
69
70
|
end
|
|
70
71
|
|
|
71
72
|
class << self
|
|
@@ -73,6 +74,11 @@ module MemoryIO
|
|
|
73
74
|
#
|
|
74
75
|
# @return [CPP::String]
|
|
75
76
|
#
|
|
77
|
+
# @raise [EOFError]
|
|
78
|
+
# The object is incomplete, or its characters cannot be read from
|
|
79
|
+
# where it points. {MemoryIO::IO#read} answers with the objects it
|
|
80
|
+
# read in full rather than one that is partly filled in.
|
|
81
|
+
#
|
|
76
82
|
# @example
|
|
77
83
|
# # echo '#include <string>\n#include <cstdio>\nint main() {' > a.cpp && \
|
|
78
84
|
# # echo 'std::string a="abcd"; printf("%p\\n", &a);' >> a.cpp && \
|
|
@@ -87,10 +93,11 @@ module MemoryIO
|
|
|
87
93
|
def read(stream)
|
|
88
94
|
dataplus = read_size_t(stream)
|
|
89
95
|
length = read_size_t(stream)
|
|
90
|
-
union =
|
|
96
|
+
union = MemoryIO::Util.read_exactly(stream, LOCAL_CAPACITY + 1)
|
|
91
97
|
if length > LOCAL_CAPACITY
|
|
92
|
-
|
|
93
|
-
|
|
98
|
+
context = MemoryIO::Context.of(stream)
|
|
99
|
+
capacity = MemoryIO::Util.unpack(union[0, context.pointer_size], context.endian)
|
|
100
|
+
data = keep_pos(stream, pos: dataplus) { |s| MemoryIO::Util.read_exactly(s, length) }
|
|
94
101
|
else
|
|
95
102
|
capacity = LOCAL_CAPACITY
|
|
96
103
|
data = union[0, length]
|
|
@@ -109,10 +116,10 @@ module MemoryIO
|
|
|
109
116
|
write_size_t(stream, obj.length)
|
|
110
117
|
pos = stream.pos
|
|
111
118
|
if obj.length > LOCAL_CAPACITY
|
|
112
|
-
keep_pos(stream, pos: obj.dataplus) { |s| s.write(obj.data
|
|
119
|
+
keep_pos(stream, pos: obj.dataplus) { |s| s.write("#{obj.data}\u0000") }
|
|
113
120
|
write_size_t(stream, obj.capacity)
|
|
114
121
|
else
|
|
115
|
-
stream.write(obj.data
|
|
122
|
+
stream.write("#{obj.data}\u0000")
|
|
116
123
|
end
|
|
117
124
|
stream.pos = pos + LOCAL_CAPACITY + 1
|
|
118
125
|
end
|
|
@@ -19,12 +19,14 @@ module MemoryIO
|
|
|
19
19
|
#
|
|
20
20
|
# @param [Object] object
|
|
21
21
|
# @param [Array<Symbol>] keys
|
|
22
|
+
# @param [Hash] option
|
|
23
|
+
# Extra options.
|
|
22
24
|
#
|
|
23
|
-
# @option [Thread::Backtrace::Location] caller
|
|
24
|
-
# This option should present if and only if +object+ is a subclass of {Types::Type}.
|
|
25
|
-
# @option [String] doc
|
|
26
|
-
#
|
|
27
|
-
# Automatically
|
|
25
|
+
# @option option [Thread::Backtrace::Location] :caller
|
|
26
|
+
# This option should be present if and only if +object+ is a subclass of {Types::Type}.
|
|
27
|
+
# @option option [String] :doc
|
|
28
|
+
# Doc-string.
|
|
29
|
+
# Automatically parsed from caller location if this option isn't present.
|
|
28
30
|
def initialize(object, keys, option = {})
|
|
29
31
|
@obj = object
|
|
30
32
|
@keys = keys
|
|
@@ -60,14 +62,14 @@ module MemoryIO
|
|
|
60
62
|
str.strip!
|
|
61
63
|
break unless str.start_with?('#')
|
|
62
64
|
|
|
63
|
-
strings.unshift(str[2
|
|
65
|
+
strings.unshift(str[2..] || '')
|
|
64
66
|
end
|
|
65
67
|
trim_docstring(strings)
|
|
66
68
|
end
|
|
67
69
|
|
|
68
70
|
def trim_docstring(strings)
|
|
69
71
|
strings = strings.drop_while { |s| s.start_with?('@') }.take_while { |s| !s.start_with?('@') }
|
|
70
|
-
strings.drop_while(&:empty?).reverse.drop_while(&:empty?).reverse.join("\n")
|
|
72
|
+
"#{strings.drop_while(&:empty?).reverse.drop_while(&:empty?).reverse.join("\n")}\n"
|
|
71
73
|
end
|
|
72
74
|
end
|
|
73
75
|
end
|
data/lib/memory_io/types/type.rb
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require '
|
|
4
|
-
|
|
3
|
+
require 'memory_io/context'
|
|
5
4
|
require 'memory_io/types/record'
|
|
6
5
|
require 'memory_io/util'
|
|
7
6
|
|
|
8
7
|
module MemoryIO
|
|
9
8
|
module Types
|
|
10
|
-
# The base class, all descendants of this class would be
|
|
9
|
+
# The base class, all descendants of this class would be considered as a valid 'type'.
|
|
11
10
|
class Type
|
|
12
|
-
# The size of +size_t
|
|
13
|
-
|
|
11
|
+
# The size of +size_t+ assumed when the stream says nothing more specific.
|
|
12
|
+
#
|
|
13
|
+
# @see MemoryIO::Context#pointer_size
|
|
14
|
+
SIZE_T = MemoryIO::Context::DEFAULT_POINTER_SIZE
|
|
14
15
|
|
|
15
16
|
class << self
|
|
16
|
-
# Read
|
|
17
|
+
# Read one +size_t+ and cast it to an unsigned integer.
|
|
18
|
+
#
|
|
19
|
+
# Its width and byte order are taken from the {MemoryIO::Context} of +stream+.
|
|
17
20
|
#
|
|
18
21
|
# @param [#read] stream
|
|
19
22
|
# Stream to read.
|
|
@@ -21,15 +24,21 @@ module MemoryIO
|
|
|
21
24
|
# @return [Integer]
|
|
22
25
|
# Result.
|
|
23
26
|
#
|
|
27
|
+
# @raise [EOFError]
|
|
28
|
+
# Fewer bytes than a +size_t+ remain in +stream+.
|
|
29
|
+
#
|
|
24
30
|
# @example
|
|
25
31
|
# s = StringIO.new("\xEF\xBE\xAD\xDExV4\x00")
|
|
26
32
|
# Type.read_size_t(s).to_s(16)
|
|
27
33
|
# #=> '345678deadbeef'
|
|
28
34
|
def read_size_t(stream)
|
|
29
|
-
MemoryIO::
|
|
35
|
+
context = MemoryIO::Context.of(stream)
|
|
36
|
+
MemoryIO::Util.unpack(MemoryIO::Util.read_exactly(stream, context.pointer_size), context.endian)
|
|
30
37
|
end
|
|
31
38
|
|
|
32
|
-
# Pack +val+ into
|
|
39
|
+
# Pack +val+ into one +size_t+ and write it to +stream+.
|
|
40
|
+
#
|
|
41
|
+
# Its width and byte order are taken from the {MemoryIO::Context} of +stream+.
|
|
33
42
|
#
|
|
34
43
|
# @param [#write] stream
|
|
35
44
|
# Stream to write.
|
|
@@ -44,11 +53,16 @@ module MemoryIO
|
|
|
44
53
|
# s.string
|
|
45
54
|
# #=> "\x23\x01\x00\x00\x00\x00\x00\x00"
|
|
46
55
|
def write_size_t(stream, val)
|
|
47
|
-
|
|
56
|
+
context = MemoryIO::Context.of(stream)
|
|
57
|
+
stream.write(MemoryIO::Util.pack(val, context.pointer_size, context.endian))
|
|
48
58
|
end
|
|
49
59
|
|
|
50
60
|
# Yield a block and resume the position of stream.
|
|
51
61
|
#
|
|
62
|
+
# The position is resumed even if the block raises,
|
|
63
|
+
# so a failure while following an invalid pointer doesn't
|
|
64
|
+
# leave the stream at an unexpected position.
|
|
65
|
+
#
|
|
52
66
|
# @param [#pos, #pos=] stream
|
|
53
67
|
# Stream.
|
|
54
68
|
# @param [Integer] pos
|
|
@@ -71,9 +85,11 @@ module MemoryIO
|
|
|
71
85
|
def keep_pos(stream, pos: nil)
|
|
72
86
|
org = stream.pos
|
|
73
87
|
stream.pos = pos if pos
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
begin
|
|
89
|
+
yield stream
|
|
90
|
+
ensure
|
|
91
|
+
stream.pos = org
|
|
92
|
+
end
|
|
77
93
|
end
|
|
78
94
|
|
|
79
95
|
# @api private
|
|
@@ -83,8 +99,9 @@ module MemoryIO
|
|
|
83
99
|
# @param [Symbol] symbol
|
|
84
100
|
# Symbol that has been registered in {.register}.
|
|
85
101
|
#
|
|
86
|
-
# @return [
|
|
87
|
-
# The
|
|
102
|
+
# @return [MemoryIO::Types::Record?]
|
|
103
|
+
# The record that was registered in {.register},
|
|
104
|
+
# or +nil+ if +symbol+ has never been registered.
|
|
88
105
|
#
|
|
89
106
|
# @see .register
|
|
90
107
|
def find(symbol)
|
|
@@ -95,10 +112,12 @@ module MemoryIO
|
|
|
95
112
|
#
|
|
96
113
|
# @param [#read, #write] object
|
|
97
114
|
# Normally, +object+ is a descendant class of {Type}.
|
|
115
|
+
# @param [Hash] option
|
|
116
|
+
# Extra options.
|
|
98
117
|
#
|
|
99
|
-
# @option [Symbol, Array<Symbol>] alias
|
|
118
|
+
# @option option [Symbol, Array<Symbol>] :alias
|
|
100
119
|
# Custom symbol name(s) that can be used in {.find}.
|
|
101
|
-
# @option [String] doc
|
|
120
|
+
# @option option [String] :doc
|
|
102
121
|
# Doc string that will be shown in README.md.
|
|
103
122
|
#
|
|
104
123
|
# @return [Array<Symbol>]
|
|
@@ -119,11 +138,11 @@ module MemoryIO
|
|
|
119
138
|
#
|
|
120
139
|
# @note
|
|
121
140
|
# If all symbols in +alias+ have been registered, an ArgumentError will be raised.
|
|
122
|
-
# However, if at least one of aliases hasn't been used, registration will
|
|
141
|
+
# However, if at least one of aliases hasn't been used, registration will succeed.
|
|
123
142
|
#
|
|
124
143
|
# @see .find
|
|
125
144
|
def register(object, option = {})
|
|
126
|
-
@map ||=
|
|
145
|
+
@map ||= {}
|
|
127
146
|
aliases = Array(option[:alias])
|
|
128
147
|
reg_fail = ArgumentError.new(<<-EOS.strip)
|
|
129
148
|
Register '#{object.inspect}' fails because another object with same name has been registered.
|
|
@@ -152,6 +171,7 @@ Specify an alias such as `register(MyClass, alias: :custom_alias_name)`.
|
|
|
152
171
|
#
|
|
153
172
|
# To record descendants.
|
|
154
173
|
def inherited(klass)
|
|
174
|
+
super
|
|
155
175
|
register(klass, caller: caller_locations(1, 1).first)
|
|
156
176
|
end
|
|
157
177
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require 'memory_io/types/type'
|
|
4
4
|
require 'memory_io/util'
|
|
5
5
|
|
|
6
|
-
Dir.glob(File.join(__dir__, '**', '*.rb')).
|
|
6
|
+
Dir.glob(File.join(__dir__, '**', '*.rb')).each { |f| require f unless f == __FILE__ }
|
|
7
7
|
|
|
8
8
|
module MemoryIO
|
|
9
9
|
# Module that includes multiple types.
|
|
@@ -18,22 +18,22 @@ module MemoryIO
|
|
|
18
18
|
#
|
|
19
19
|
# This method will search all descendants of {Types::Type}.
|
|
20
20
|
#
|
|
21
|
-
# @
|
|
21
|
+
# @param [Symbol] name
|
|
22
22
|
# Class name to be searched.
|
|
23
23
|
#
|
|
24
24
|
# @return [#read, #write]
|
|
25
25
|
# Any object that implemented method +read+ and +write+.
|
|
26
|
-
# Usually returns a class
|
|
26
|
+
# Usually returns a class inherited from {Types::Type}.
|
|
27
27
|
#
|
|
28
28
|
# @example
|
|
29
29
|
# Types.find(:c_str)
|
|
30
|
-
# #=> MemoryIO::Types::CStr
|
|
30
|
+
# #=> MemoryIO::Types::Clang::CStr
|
|
31
31
|
#
|
|
32
32
|
# Types.find(:u64)
|
|
33
33
|
# #=> #<MemoryIO::Types::Number:0x000055ecc017a310 @bytes=8, @pack_str="Q", @signed=false>
|
|
34
34
|
def find(name)
|
|
35
35
|
obj = Types::Type.find(name)
|
|
36
|
-
|
|
36
|
+
obj&.obj
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
# @api private
|
|
@@ -50,7 +50,7 @@ module MemoryIO
|
|
|
50
50
|
#
|
|
51
51
|
# @example
|
|
52
52
|
# Types.get_proc(:c_str, :write)
|
|
53
|
-
# #=> #<Method: MemoryIO::Types::CStr.write>
|
|
53
|
+
# #=> #<Method: MemoryIO::Types::Clang::CStr.write>
|
|
54
54
|
# Types.get_proc(:s32, :read)
|
|
55
55
|
# #=> #<Method: MemoryIO::Types::Number#read>
|
|
56
56
|
def get_proc(name, rw)
|