fiddle 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +52 -1
- data/Rakefile +3 -5
- data/bin/downloader.rb +331 -0
- data/bin/extlibs.rb +262 -0
- data/ext/fiddle/closure.c +5 -4
- data/ext/fiddle/conversions.c +161 -16
- data/ext/fiddle/conversions.h +14 -4
- data/ext/fiddle/depend +83 -0
- data/ext/fiddle/extconf.rb +54 -28
- data/ext/fiddle/extlibs +10 -2
- data/ext/fiddle/fiddle.c +46 -35
- data/ext/fiddle/fiddle.h +35 -1
- data/ext/fiddle/function.c +220 -81
- data/ext/fiddle/handle.c +10 -12
- data/ext/fiddle/pinned.c +123 -0
- data/ext/fiddle/pointer.c +106 -24
- data/ext/fiddle/win32/fficonfig.h +0 -0
- data/ext/fiddle/win32/libffi-config.rb +1 -1
- data/ext/fiddle/win32/libffi.mk.tmpl +0 -0
- data/fiddle.gemspec +46 -4
- data/lib/fiddle.rb +3 -1
- data/lib/fiddle/cparser.rb +29 -2
- data/lib/fiddle/import.rb +11 -9
- data/lib/fiddle/pack.rb +14 -7
- data/lib/fiddle/struct.rb +267 -43
- data/lib/fiddle/value.rb +18 -9
- data/lib/fiddle/version.rb +3 -0
- metadata +13 -12
- data/.gitignore +0 -13
- data/.travis.yml +0 -7
- data/Gemfile +0 -4
- data/bin/console +0 -14
- data/bin/setup +0 -8
File without changes
|
@@ -32,7 +32,7 @@ IO.foreach("#{srcdir}/configure.ac") do |line|
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
builddir = srcdir == "." ? enable['builddir'] : "."
|
35
|
+
builddir = srcdir == "." ? (enable['builddir'] || ".") : "."
|
36
36
|
conf['TARGET'] = /^x64/ =~ host ? "X86_WIN64" : "X86_WIN32"
|
37
37
|
|
38
38
|
FileUtils.mkdir_p([builddir, "#{builddir}/include", "#{builddir}/src/x86"])
|
File without changes
|
data/fiddle.gemspec
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
3
|
+
version_module = Module.new do
|
4
|
+
version_rb = File.join(__dir__, "lib/fiddle/version.rb")
|
5
|
+
module_eval(File.read(version_rb), version_rb, __LINE__)
|
6
|
+
end
|
7
|
+
|
2
8
|
Gem::Specification.new do |spec|
|
3
9
|
spec.name = "fiddle"
|
4
|
-
spec.version =
|
10
|
+
spec.version = version_module::Fiddle::VERSION
|
5
11
|
spec.authors = ["Aaron Patterson", "SHIBATA Hiroshi"]
|
6
12
|
spec.email = ["aaron@tenderlovemaking.com", "hsbt@ruby-lang.org"]
|
7
13
|
|
@@ -10,14 +16,50 @@ Gem::Specification.new do |spec|
|
|
10
16
|
spec.homepage = "https://github.com/ruby/fiddle"
|
11
17
|
spec.license = "BSD-2-Clause"
|
12
18
|
|
13
|
-
spec.files = [
|
14
|
-
|
15
|
-
|
19
|
+
spec.files = [
|
20
|
+
"LICENSE.txt",
|
21
|
+
"README.md",
|
22
|
+
"Rakefile",
|
23
|
+
"bin/downloader.rb",
|
24
|
+
"bin/extlibs.rb",
|
25
|
+
"ext/fiddle/closure.c",
|
26
|
+
"ext/fiddle/closure.h",
|
27
|
+
"ext/fiddle/conversions.c",
|
28
|
+
"ext/fiddle/conversions.h",
|
29
|
+
"ext/fiddle/depend",
|
30
|
+
"ext/fiddle/extconf.rb",
|
31
|
+
"ext/fiddle/extlibs",
|
32
|
+
"ext/fiddle/fiddle.c",
|
33
|
+
"ext/fiddle/fiddle.h",
|
34
|
+
"ext/fiddle/function.c",
|
35
|
+
"ext/fiddle/function.h",
|
36
|
+
"ext/fiddle/handle.c",
|
37
|
+
"ext/fiddle/pinned.c",
|
38
|
+
"ext/fiddle/pointer.c",
|
39
|
+
"ext/fiddle/win32/fficonfig.h",
|
40
|
+
"ext/fiddle/win32/libffi-3.2.1-mswin.patch",
|
41
|
+
"ext/fiddle/win32/libffi-config.rb",
|
42
|
+
"ext/fiddle/win32/libffi.mk.tmpl",
|
43
|
+
"fiddle.gemspec",
|
44
|
+
"lib/fiddle.rb",
|
45
|
+
"lib/fiddle/closure.rb",
|
46
|
+
"lib/fiddle/cparser.rb",
|
47
|
+
"lib/fiddle/function.rb",
|
48
|
+
"lib/fiddle/import.rb",
|
49
|
+
"lib/fiddle/pack.rb",
|
50
|
+
"lib/fiddle/struct.rb",
|
51
|
+
"lib/fiddle/types.rb",
|
52
|
+
"lib/fiddle/value.rb",
|
53
|
+
"lib/fiddle/version.rb",
|
54
|
+
]
|
16
55
|
spec.require_paths = ["lib"]
|
56
|
+
spec.extensions = ["ext/fiddle/extconf.rb"]
|
17
57
|
|
18
58
|
spec.required_ruby_version = ">= 2.3.0"
|
19
59
|
|
20
60
|
spec.add_development_dependency "bundler"
|
21
61
|
spec.add_development_dependency "rake"
|
22
62
|
spec.add_development_dependency "rake-compiler"
|
63
|
+
|
64
|
+
spec.metadata["msys2_mingw_dependencies"] = "libffi"
|
23
65
|
end
|
data/lib/fiddle.rb
CHANGED
data/lib/fiddle/cparser.rb
CHANGED
@@ -35,12 +35,37 @@ module Fiddle
|
|
35
35
|
def parse_struct_signature(signature, tymap=nil)
|
36
36
|
if signature.is_a?(String)
|
37
37
|
signature = split_arguments(signature, /[,;]/)
|
38
|
+
elsif signature.is_a?(Hash)
|
39
|
+
signature = [signature]
|
38
40
|
end
|
39
41
|
mems = []
|
40
42
|
tys = []
|
41
43
|
signature.each{|msig|
|
42
|
-
msig = compact(msig)
|
44
|
+
msig = compact(msig) if msig.is_a?(String)
|
43
45
|
case msig
|
46
|
+
when Hash
|
47
|
+
msig.each do |struct_name, struct_signature|
|
48
|
+
struct_name = struct_name.to_s if struct_name.is_a?(Symbol)
|
49
|
+
struct_name = compact(struct_name)
|
50
|
+
struct_count = nil
|
51
|
+
if struct_name =~ /^([\w\*\s]+)\[(\d+)\]$/
|
52
|
+
struct_count = $2.to_i
|
53
|
+
struct_name = $1
|
54
|
+
end
|
55
|
+
if struct_signature.respond_to?(:entity_class)
|
56
|
+
struct_type = struct_signature
|
57
|
+
else
|
58
|
+
parsed_struct = parse_struct_signature(struct_signature, tymap)
|
59
|
+
struct_type = CStructBuilder.create(CStruct, *parsed_struct)
|
60
|
+
end
|
61
|
+
if struct_count
|
62
|
+
ty = [struct_type, struct_count]
|
63
|
+
else
|
64
|
+
ty = struct_type
|
65
|
+
end
|
66
|
+
mems.push([struct_name, struct_type.members])
|
67
|
+
tys.push(ty)
|
68
|
+
end
|
44
69
|
when /^[\w\*\s]+[\*\s](\w+)$/
|
45
70
|
mems.push($1)
|
46
71
|
tys.push(parse_ctype(msig, tymap))
|
@@ -172,6 +197,8 @@ module Fiddle
|
|
172
197
|
return TYPE_UINTPTR_T
|
173
198
|
when /\*/, /\[[\s\d]*\]/
|
174
199
|
return TYPE_VOIDP
|
200
|
+
when "..."
|
201
|
+
return TYPE_VARIADIC
|
175
202
|
else
|
176
203
|
ty = ty.split(' ', 2)[0]
|
177
204
|
if( tymap[ty] )
|
@@ -186,7 +213,7 @@ module Fiddle
|
|
186
213
|
|
187
214
|
def split_arguments(arguments, sep=',')
|
188
215
|
return [] if arguments.strip == 'void'
|
189
|
-
arguments.scan(/([\w\*\s]+\(\*\w*\)\(.*?\)|[\w\*\s\[\]]
|
216
|
+
arguments.scan(/([\w\*\s]+\(\*\w*\)\(.*?\)|[\w\*\s\[\]]+|\.\.\.)(?:#{sep}\s*|$)/).collect {|m| m[0]}
|
190
217
|
end
|
191
218
|
|
192
219
|
def compact(signature)
|
data/lib/fiddle/import.rb
CHANGED
@@ -83,11 +83,7 @@ module Fiddle
|
|
83
83
|
when Importer
|
84
84
|
lib.handlers
|
85
85
|
else
|
86
|
-
|
87
|
-
Fiddle.dlopen(lib)
|
88
|
-
rescue DLError
|
89
|
-
raise(DLError, "can't load #{lib}")
|
90
|
-
end
|
86
|
+
Fiddle.dlopen(lib)
|
91
87
|
end
|
92
88
|
}.flatten()
|
93
89
|
@handler = CompositeHandler.new(handles)
|
@@ -115,16 +111,21 @@ module Fiddle
|
|
115
111
|
return SIZEOF_INT
|
116
112
|
when TYPE_LONG
|
117
113
|
return SIZEOF_LONG
|
118
|
-
when TYPE_LONG_LONG
|
119
|
-
return SIZEOF_LONG_LONG
|
120
114
|
when TYPE_FLOAT
|
121
115
|
return SIZEOF_FLOAT
|
122
116
|
when TYPE_DOUBLE
|
123
117
|
return SIZEOF_DOUBLE
|
124
118
|
when TYPE_VOIDP
|
125
119
|
return SIZEOF_VOIDP
|
120
|
+
when TYPE_CONST_STRING
|
121
|
+
return SIZEOF_CONST_STRING
|
126
122
|
else
|
127
|
-
|
123
|
+
if defined?(TYPE_LONG_LONG) and
|
124
|
+
ty == TYPE_LONG_LONG
|
125
|
+
return SIZEOF_LONG_LONG
|
126
|
+
else
|
127
|
+
raise(DLError, "unknown type: #{ty}")
|
128
|
+
end
|
128
129
|
end
|
129
130
|
when Class
|
130
131
|
if( ty.instance_methods().include?(:to_ptr) )
|
@@ -154,7 +155,8 @@ module Fiddle
|
|
154
155
|
# :stopdoc:
|
155
156
|
CALL_TYPE_TO_ABI = Hash.new { |h, k|
|
156
157
|
raise RuntimeError, "unsupported call type: #{k}"
|
157
|
-
}.merge({ :stdcall => (
|
158
|
+
}.merge({ :stdcall => Function.const_defined?(:STDCALL) ? Function::STDCALL :
|
159
|
+
Function::DEFAULT,
|
158
160
|
:cdecl => Function::DEFAULT,
|
159
161
|
nil => Function::DEFAULT
|
160
162
|
}).freeze
|
data/lib/fiddle/pack.rb
CHANGED
@@ -18,7 +18,7 @@ module Fiddle
|
|
18
18
|
}
|
19
19
|
|
20
20
|
PACK_MAP = {
|
21
|
-
TYPE_VOIDP =>
|
21
|
+
TYPE_VOIDP => "l!",
|
22
22
|
TYPE_CHAR => "c",
|
23
23
|
TYPE_SHORT => "s!",
|
24
24
|
TYPE_INT => "i!",
|
@@ -48,6 +48,7 @@ module Fiddle
|
|
48
48
|
ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[-TYPE_LONG_LONG] = ALIGN_LONG_LONG
|
49
49
|
PACK_MAP[TYPE_LONG_LONG] = PACK_MAP[-TYPE_LONG_LONG] = "q"
|
50
50
|
SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[-TYPE_LONG_LONG] = SIZEOF_LONG_LONG
|
51
|
+
PACK_MAP[TYPE_VOIDP] = "q" if SIZEOF_LONG_LONG == SIZEOF_VOIDP
|
51
52
|
end
|
52
53
|
|
53
54
|
def align(addr, align)
|
@@ -80,10 +81,13 @@ module Fiddle
|
|
80
81
|
case SIZEOF_VOIDP
|
81
82
|
when SIZEOF_LONG
|
82
83
|
ary.pack(@template)
|
83
|
-
when SIZEOF_LONG_LONG
|
84
|
-
ary.pack(@template)
|
85
84
|
else
|
86
|
-
|
85
|
+
if defined?(TYPE_LONG_LONG) and
|
86
|
+
SIZEOF_VOIDP == SIZEOF_LONG_LONG
|
87
|
+
ary.pack(@template)
|
88
|
+
else
|
89
|
+
raise(RuntimeError, "sizeof(void*)?")
|
90
|
+
end
|
87
91
|
end
|
88
92
|
end
|
89
93
|
|
@@ -91,10 +95,13 @@ module Fiddle
|
|
91
95
|
case SIZEOF_VOIDP
|
92
96
|
when SIZEOF_LONG
|
93
97
|
ary.join().unpack(@template)
|
94
|
-
when SIZEOF_LONG_LONG
|
95
|
-
ary.join().unpack(@template)
|
96
98
|
else
|
97
|
-
|
99
|
+
if defined?(TYPE_LONG_LONG) and
|
100
|
+
SIZEOF_VOIDP == SIZEOF_LONG_LONG
|
101
|
+
ary.join().unpack(@template)
|
102
|
+
else
|
103
|
+
raise(RuntimeError, "sizeof(void*)?")
|
104
|
+
end
|
98
105
|
end
|
99
106
|
end
|
100
107
|
|
data/lib/fiddle/struct.rb
CHANGED
@@ -4,15 +4,72 @@ require 'fiddle/value'
|
|
4
4
|
require 'fiddle/pack'
|
5
5
|
|
6
6
|
module Fiddle
|
7
|
-
# C
|
7
|
+
# A base class for objects representing a C structure
|
8
8
|
class CStruct
|
9
|
+
include Enumerable
|
10
|
+
|
9
11
|
# accessor to Fiddle::CStructEntity
|
10
12
|
def CStruct.entity_class
|
11
13
|
CStructEntity
|
12
14
|
end
|
15
|
+
|
16
|
+
def each
|
17
|
+
return enum_for(__function__) unless block_given?
|
18
|
+
|
19
|
+
self.class.members.each do |name,|
|
20
|
+
yield(self[name])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def each_pair
|
25
|
+
return enum_for(__function__) unless block_given?
|
26
|
+
|
27
|
+
self.class.members.each do |name,|
|
28
|
+
yield(name, self[name])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_h
|
33
|
+
hash = {}
|
34
|
+
each_pair do |name, value|
|
35
|
+
hash[name] = unstruct(value)
|
36
|
+
end
|
37
|
+
hash
|
38
|
+
end
|
39
|
+
|
40
|
+
def replace(another)
|
41
|
+
if another.nil?
|
42
|
+
self.class.members.each do |name,|
|
43
|
+
self[name] = nil
|
44
|
+
end
|
45
|
+
elsif another.respond_to?(:each_pair)
|
46
|
+
another.each_pair do |name, value|
|
47
|
+
self[name] = value
|
48
|
+
end
|
49
|
+
else
|
50
|
+
another.each do |name, value|
|
51
|
+
self[name] = value
|
52
|
+
end
|
53
|
+
end
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def unstruct(value)
|
59
|
+
case value
|
60
|
+
when CStruct
|
61
|
+
value.to_h
|
62
|
+
when Array
|
63
|
+
value.collect do |v|
|
64
|
+
unstruct(v)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
value
|
68
|
+
end
|
69
|
+
end
|
13
70
|
end
|
14
71
|
|
15
|
-
# C union
|
72
|
+
# A base class for objects representing a C union
|
16
73
|
class CUnion
|
17
74
|
# accessor to Fiddle::CUnionEntity
|
18
75
|
def CUnion.entity_class
|
@@ -20,6 +77,41 @@ module Fiddle
|
|
20
77
|
end
|
21
78
|
end
|
22
79
|
|
80
|
+
# Wrapper for arrays within a struct
|
81
|
+
class StructArray < Array
|
82
|
+
include ValueUtil
|
83
|
+
|
84
|
+
def initialize(ptr, type, initial_values)
|
85
|
+
@ptr = ptr
|
86
|
+
@type = type
|
87
|
+
@is_struct = @type.respond_to?(:entity_class)
|
88
|
+
if @is_struct
|
89
|
+
super(initial_values)
|
90
|
+
else
|
91
|
+
@size = Fiddle::PackInfo::SIZE_MAP[type]
|
92
|
+
@pack_format = Fiddle::PackInfo::PACK_MAP[type]
|
93
|
+
super(initial_values.collect { |v| unsigned_value(v, type) })
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_ptr
|
98
|
+
@ptr
|
99
|
+
end
|
100
|
+
|
101
|
+
def []=(index, value)
|
102
|
+
if index < 0 || index >= size
|
103
|
+
raise IndexError, 'index %d outside of array bounds 0...%d' % [index, size]
|
104
|
+
end
|
105
|
+
|
106
|
+
if @is_struct
|
107
|
+
self[index].replace(value)
|
108
|
+
else
|
109
|
+
to_ptr[index * @size, @size] = [value].pack(@pack_format)
|
110
|
+
super(index, value)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
23
115
|
# Used to construct C classes (CUnion, CStruct, etc)
|
24
116
|
#
|
25
117
|
# Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an
|
@@ -35,7 +127,7 @@ module Fiddle
|
|
35
127
|
# Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an
|
36
128
|
# easy-to-use manner.
|
37
129
|
#
|
38
|
-
#
|
130
|
+
# Examples:
|
39
131
|
#
|
40
132
|
# require 'fiddle/struct'
|
41
133
|
# require 'fiddle/cparser'
|
@@ -46,47 +138,98 @@ module Fiddle
|
|
46
138
|
#
|
47
139
|
# MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members)
|
48
140
|
#
|
49
|
-
#
|
141
|
+
# MyStruct.malloc(Fiddle::RUBY_FREE) do |obj|
|
142
|
+
# ...
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# obj = MyStruct.malloc(Fiddle::RUBY_FREE)
|
146
|
+
# begin
|
147
|
+
# ...
|
148
|
+
# ensure
|
149
|
+
# obj.call_free
|
150
|
+
# end
|
151
|
+
#
|
152
|
+
# obj = MyStruct.malloc
|
153
|
+
# begin
|
154
|
+
# ...
|
155
|
+
# ensure
|
156
|
+
# Fiddle.free obj.to_ptr
|
157
|
+
# end
|
50
158
|
#
|
51
159
|
def create(klass, types, members)
|
52
160
|
new_class = Class.new(klass){
|
53
|
-
define_method(:initialize){|addr|
|
54
|
-
|
161
|
+
define_method(:initialize){|addr, func = nil|
|
162
|
+
if addr.is_a?(self.class.entity_class)
|
163
|
+
@entity = addr
|
164
|
+
else
|
165
|
+
@entity = self.class.entity_class.new(addr, types, func)
|
166
|
+
end
|
55
167
|
@entity.assign_names(members)
|
56
168
|
}
|
169
|
+
define_method(:[]) { |*args| @entity.send(:[], *args) }
|
170
|
+
define_method(:[]=) { |*args| @entity.send(:[]=, *args) }
|
57
171
|
define_method(:to_ptr){ @entity }
|
58
172
|
define_method(:to_i){ @entity.to_i }
|
173
|
+
define_singleton_method(:types) { types }
|
174
|
+
define_singleton_method(:members) { members }
|
59
175
|
members.each{|name|
|
176
|
+
name = name[0] if name.is_a?(Array) # name is a nested struct
|
177
|
+
next if method_defined?(name)
|
60
178
|
define_method(name){ @entity[name] }
|
61
179
|
define_method(name + "="){|val| @entity[name] = val }
|
62
180
|
}
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
181
|
+
entity_class = klass.entity_class
|
182
|
+
alignment = entity_class.alignment(types)
|
183
|
+
size = entity_class.size(types)
|
184
|
+
define_singleton_method(:alignment) { alignment }
|
185
|
+
define_singleton_method(:size) { size }
|
186
|
+
define_singleton_method(:malloc) do |func=nil, &block|
|
187
|
+
if block
|
188
|
+
entity_class.malloc(types, func, size) do |entity|
|
189
|
+
block.call(new(entity))
|
190
|
+
end
|
191
|
+
else
|
192
|
+
new(entity_class.malloc(types, func, size))
|
193
|
+
end
|
68
194
|
end
|
69
|
-
|
70
|
-
addr = Fiddle.malloc(#{size})
|
71
|
-
new(addr)
|
72
|
-
end
|
73
|
-
EOS
|
195
|
+
}
|
74
196
|
return new_class
|
75
197
|
end
|
76
198
|
module_function :create
|
77
199
|
end
|
78
200
|
|
79
|
-
# A C
|
201
|
+
# A pointer to a C structure
|
80
202
|
class CStructEntity < Fiddle::Pointer
|
81
203
|
include PackInfo
|
82
204
|
include ValueUtil
|
83
205
|
|
206
|
+
def CStructEntity.alignment(types)
|
207
|
+
max = 1
|
208
|
+
types.each do |type, count = 1|
|
209
|
+
if type.respond_to?(:entity_class)
|
210
|
+
n = type.alignment
|
211
|
+
else
|
212
|
+
n = ALIGN_MAP[type]
|
213
|
+
end
|
214
|
+
max = n if n > max
|
215
|
+
end
|
216
|
+
max
|
217
|
+
end
|
218
|
+
|
84
219
|
# Allocates a C struct with the +types+ provided.
|
85
220
|
#
|
86
|
-
#
|
87
|
-
def CStructEntity.malloc(types, func = nil)
|
88
|
-
|
89
|
-
|
221
|
+
# See Fiddle::Pointer.malloc for memory management issues.
|
222
|
+
def CStructEntity.malloc(types, func = nil, size = size(types), &block)
|
223
|
+
if block_given?
|
224
|
+
super(size, func) do |struct|
|
225
|
+
struct.set_ctypes types
|
226
|
+
yield struct
|
227
|
+
end
|
228
|
+
else
|
229
|
+
struct = super(size, func)
|
230
|
+
struct.set_ctypes types
|
231
|
+
struct
|
232
|
+
end
|
90
233
|
end
|
91
234
|
|
92
235
|
# Returns the offset for the packed sizes for the given +types+.
|
@@ -102,9 +245,15 @@ module Fiddle
|
|
102
245
|
max_align = types.map { |type, count = 1|
|
103
246
|
last_offset = offset
|
104
247
|
|
105
|
-
|
248
|
+
if type.respond_to?(:entity_class)
|
249
|
+
align = type.alignment
|
250
|
+
type_size = type.size
|
251
|
+
else
|
252
|
+
align = PackInfo::ALIGN_MAP[type]
|
253
|
+
type_size = PackInfo::SIZE_MAP[type]
|
254
|
+
end
|
106
255
|
offset = PackInfo.align(last_offset, align) +
|
107
|
-
(
|
256
|
+
(type_size * count)
|
108
257
|
|
109
258
|
align
|
110
259
|
}.max
|
@@ -118,13 +267,37 @@ module Fiddle
|
|
118
267
|
#
|
119
268
|
# See also Fiddle::Pointer.new
|
120
269
|
def initialize(addr, types, func = nil)
|
270
|
+
if func && addr.is_a?(Pointer) && addr.free
|
271
|
+
raise ArgumentError, 'free function specified on both underlying struct Pointer and when creating a CStructEntity - who do you want to free this?'
|
272
|
+
end
|
121
273
|
set_ctypes(types)
|
122
274
|
super(addr, @size, func)
|
123
275
|
end
|
124
276
|
|
125
277
|
# Set the names of the +members+ in this C struct
|
126
278
|
def assign_names(members)
|
127
|
-
@members =
|
279
|
+
@members = []
|
280
|
+
@nested_structs = {}
|
281
|
+
members.each_with_index do |member, index|
|
282
|
+
if member.is_a?(Array) # nested struct
|
283
|
+
member_name = member[0]
|
284
|
+
struct_type, struct_count = @ctypes[index]
|
285
|
+
if struct_count.nil?
|
286
|
+
struct = struct_type.new(to_i + @offset[index])
|
287
|
+
else
|
288
|
+
structs = struct_count.times.map do |i|
|
289
|
+
struct_type.new(to_i + @offset[index] + i * struct_type.size)
|
290
|
+
end
|
291
|
+
struct = StructArray.new(to_i + @offset[index],
|
292
|
+
struct_type,
|
293
|
+
structs)
|
294
|
+
end
|
295
|
+
@nested_structs[member_name] = struct
|
296
|
+
else
|
297
|
+
member_name = member
|
298
|
+
end
|
299
|
+
@members << member_name
|
300
|
+
end
|
128
301
|
end
|
129
302
|
|
130
303
|
# Calculates the offsets and sizes for the given +types+ in the struct.
|
@@ -135,12 +308,18 @@ module Fiddle
|
|
135
308
|
|
136
309
|
max_align = types.map { |type, count = 1|
|
137
310
|
orig_offset = offset
|
138
|
-
|
311
|
+
if type.respond_to?(:entity_class)
|
312
|
+
align = type.alignment
|
313
|
+
type_size = type.size
|
314
|
+
else
|
315
|
+
align = ALIGN_MAP[type]
|
316
|
+
type_size = SIZE_MAP[type]
|
317
|
+
end
|
139
318
|
offset = PackInfo.align(orig_offset, align)
|
140
319
|
|
141
320
|
@offset << offset
|
142
321
|
|
143
|
-
offset += (
|
322
|
+
offset += (type_size * count)
|
144
323
|
|
145
324
|
align
|
146
325
|
}.max
|
@@ -148,15 +327,34 @@ module Fiddle
|
|
148
327
|
@size = PackInfo.align(offset, max_align)
|
149
328
|
end
|
150
329
|
|
151
|
-
# Fetch struct member +name+
|
152
|
-
|
330
|
+
# Fetch struct member +name+ if only one argument is specified. If two
|
331
|
+
# arguments are specified, the first is an offset and the second is a
|
332
|
+
# length and this method returns the string of +length+ bytes beginning at
|
333
|
+
# +offset+.
|
334
|
+
#
|
335
|
+
# Examples:
|
336
|
+
#
|
337
|
+
# my_struct = struct(['int id']).malloc
|
338
|
+
# my_struct.id = 1
|
339
|
+
# my_struct['id'] # => 1
|
340
|
+
# my_struct[0, 4] # => "\x01\x00\x00\x00".b
|
341
|
+
#
|
342
|
+
def [](*args)
|
343
|
+
return super(*args) if args.size > 1
|
344
|
+
name = args[0]
|
153
345
|
idx = @members.index(name)
|
154
346
|
if( idx.nil? )
|
155
347
|
raise(ArgumentError, "no such member: #{name}")
|
156
348
|
end
|
157
349
|
ty = @ctypes[idx]
|
158
350
|
if( ty.is_a?(Array) )
|
159
|
-
|
351
|
+
if ty.first.respond_to?(:entity_class)
|
352
|
+
return @nested_structs[name]
|
353
|
+
else
|
354
|
+
r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1])
|
355
|
+
end
|
356
|
+
elsif ty.respond_to?(:entity_class)
|
357
|
+
return @nested_structs[name]
|
160
358
|
else
|
161
359
|
r = super(@offset[idx], SIZE_MAP[ty.abs])
|
162
360
|
end
|
@@ -176,14 +374,44 @@ module Fiddle
|
|
176
374
|
if( ty.is_a?(Integer) && (ty < 0) )
|
177
375
|
return unsigned_value(val, ty)
|
178
376
|
elsif( ty.is_a?(Array) && (ty[0] < 0) )
|
179
|
-
return
|
377
|
+
return StructArray.new(self + @offset[idx], ty[0], val)
|
180
378
|
else
|
181
379
|
return val
|
182
380
|
end
|
183
381
|
end
|
184
382
|
|
185
|
-
# Set struct member +name+, to value +val
|
186
|
-
|
383
|
+
# Set struct member +name+, to value +val+. If more arguments are
|
384
|
+
# specified, writes the string of bytes to the memory at the given
|
385
|
+
# +offset+ and +length+.
|
386
|
+
#
|
387
|
+
# Examples:
|
388
|
+
#
|
389
|
+
# my_struct = struct(['int id']).malloc
|
390
|
+
# my_struct['id'] = 1
|
391
|
+
# my_struct[0, 4] = "\x01\x00\x00\x00".b
|
392
|
+
# my_struct.id # => 1
|
393
|
+
#
|
394
|
+
def []=(*args)
|
395
|
+
return super(*args) if args.size > 2
|
396
|
+
name, val = *args
|
397
|
+
name = name.to_s if name.is_a?(Symbol)
|
398
|
+
nested_struct = @nested_structs[name]
|
399
|
+
if nested_struct
|
400
|
+
if nested_struct.is_a?(StructArray)
|
401
|
+
if val.nil?
|
402
|
+
nested_struct.each do |s|
|
403
|
+
s.replace(nil)
|
404
|
+
end
|
405
|
+
else
|
406
|
+
val.each_with_index do |v, i|
|
407
|
+
nested_struct[i] = v
|
408
|
+
end
|
409
|
+
end
|
410
|
+
else
|
411
|
+
nested_struct.replace(val)
|
412
|
+
end
|
413
|
+
return val
|
414
|
+
end
|
187
415
|
idx = @members.index(name)
|
188
416
|
if( idx.nil? )
|
189
417
|
raise(ArgumentError, "no such member: #{name}")
|
@@ -202,23 +430,16 @@ module Fiddle
|
|
202
430
|
end
|
203
431
|
end
|
204
432
|
|
433
|
+
undef_method :size=
|
205
434
|
def to_s() # :nodoc:
|
206
435
|
super(@size)
|
207
436
|
end
|
208
437
|
end
|
209
438
|
|
210
|
-
# A C union
|
439
|
+
# A pointer to a C union
|
211
440
|
class CUnionEntity < CStructEntity
|
212
441
|
include PackInfo
|
213
442
|
|
214
|
-
# Allocates a C union the +types+ provided.
|
215
|
-
#
|
216
|
-
# When the instance is garbage collected, the C function +func+ is called.
|
217
|
-
def CUnionEntity.malloc(types, func=nil)
|
218
|
-
addr = Fiddle.malloc(CUnionEntity.size(types))
|
219
|
-
CUnionEntity.new(addr, types, func)
|
220
|
-
end
|
221
|
-
|
222
443
|
# Returns the size needed for the union with the given +types+.
|
223
444
|
#
|
224
445
|
# Fiddle::CUnionEntity.size(
|
@@ -228,7 +449,11 @@ module Fiddle
|
|
228
449
|
# Fiddle::TYPE_VOIDP ]) #=> 8
|
229
450
|
def CUnionEntity.size(types)
|
230
451
|
types.map { |type, count = 1|
|
231
|
-
|
452
|
+
if type.respond_to?(:entity_class)
|
453
|
+
type.size * count
|
454
|
+
else
|
455
|
+
PackInfo::SIZE_MAP[type] * count
|
456
|
+
end
|
232
457
|
}.max
|
233
458
|
end
|
234
459
|
|
@@ -241,4 +466,3 @@ module Fiddle
|
|
241
466
|
end
|
242
467
|
end
|
243
468
|
end
|
244
|
-
|