fiddle 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: false
2
+ require 'fiddle'
3
+
4
+ module Fiddle
5
+ module PackInfo # :nodoc: all
6
+ ALIGN_MAP = {
7
+ TYPE_VOIDP => ALIGN_VOIDP,
8
+ TYPE_CHAR => ALIGN_CHAR,
9
+ TYPE_SHORT => ALIGN_SHORT,
10
+ TYPE_INT => ALIGN_INT,
11
+ TYPE_LONG => ALIGN_LONG,
12
+ TYPE_FLOAT => ALIGN_FLOAT,
13
+ TYPE_DOUBLE => ALIGN_DOUBLE,
14
+ -TYPE_CHAR => ALIGN_CHAR,
15
+ -TYPE_SHORT => ALIGN_SHORT,
16
+ -TYPE_INT => ALIGN_INT,
17
+ -TYPE_LONG => ALIGN_LONG,
18
+ }
19
+
20
+ PACK_MAP = {
21
+ TYPE_VOIDP => ((SIZEOF_VOIDP == SIZEOF_LONG_LONG) ? "q" : "l!"),
22
+ TYPE_CHAR => "c",
23
+ TYPE_SHORT => "s!",
24
+ TYPE_INT => "i!",
25
+ TYPE_LONG => "l!",
26
+ TYPE_FLOAT => "f",
27
+ TYPE_DOUBLE => "d",
28
+ -TYPE_CHAR => "c",
29
+ -TYPE_SHORT => "s!",
30
+ -TYPE_INT => "i!",
31
+ -TYPE_LONG => "l!",
32
+ }
33
+
34
+ SIZE_MAP = {
35
+ TYPE_VOIDP => SIZEOF_VOIDP,
36
+ TYPE_CHAR => SIZEOF_CHAR,
37
+ TYPE_SHORT => SIZEOF_SHORT,
38
+ TYPE_INT => SIZEOF_INT,
39
+ TYPE_LONG => SIZEOF_LONG,
40
+ TYPE_FLOAT => SIZEOF_FLOAT,
41
+ TYPE_DOUBLE => SIZEOF_DOUBLE,
42
+ -TYPE_CHAR => SIZEOF_CHAR,
43
+ -TYPE_SHORT => SIZEOF_SHORT,
44
+ -TYPE_INT => SIZEOF_INT,
45
+ -TYPE_LONG => SIZEOF_LONG,
46
+ }
47
+ if defined?(TYPE_LONG_LONG)
48
+ ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[-TYPE_LONG_LONG] = ALIGN_LONG_LONG
49
+ PACK_MAP[TYPE_LONG_LONG] = PACK_MAP[-TYPE_LONG_LONG] = "q"
50
+ SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[-TYPE_LONG_LONG] = SIZEOF_LONG_LONG
51
+ end
52
+
53
+ def align(addr, align)
54
+ d = addr % align
55
+ if( d == 0 )
56
+ addr
57
+ else
58
+ addr + (align - d)
59
+ end
60
+ end
61
+ module_function :align
62
+ end
63
+
64
+ class Packer # :nodoc: all
65
+ include PackInfo
66
+
67
+ def self.[](*types)
68
+ new(types)
69
+ end
70
+
71
+ def initialize(types)
72
+ parse_types(types)
73
+ end
74
+
75
+ def size()
76
+ @size
77
+ end
78
+
79
+ def pack(ary)
80
+ case SIZEOF_VOIDP
81
+ when SIZEOF_LONG
82
+ ary.pack(@template)
83
+ when SIZEOF_LONG_LONG
84
+ ary.pack(@template)
85
+ else
86
+ raise(RuntimeError, "sizeof(void*)?")
87
+ end
88
+ end
89
+
90
+ def unpack(ary)
91
+ case SIZEOF_VOIDP
92
+ when SIZEOF_LONG
93
+ ary.join().unpack(@template)
94
+ when SIZEOF_LONG_LONG
95
+ ary.join().unpack(@template)
96
+ else
97
+ raise(RuntimeError, "sizeof(void*)?")
98
+ end
99
+ end
100
+
101
+ private
102
+
103
+ def parse_types(types)
104
+ @template = ""
105
+ addr = 0
106
+ types.each{|t|
107
+ orig_addr = addr
108
+ if( t.is_a?(Array) )
109
+ addr = align(orig_addr, ALIGN_MAP[TYPE_VOIDP])
110
+ else
111
+ addr = align(orig_addr, ALIGN_MAP[t])
112
+ end
113
+ d = addr - orig_addr
114
+ if( d > 0 )
115
+ @template << "x#{d}"
116
+ end
117
+ if( t.is_a?(Array) )
118
+ @template << (PACK_MAP[t[0]] * t[1])
119
+ addr += (SIZE_MAP[t[0]] * t[1])
120
+ else
121
+ @template << PACK_MAP[t]
122
+ addr += SIZE_MAP[t]
123
+ end
124
+ }
125
+ addr = align(addr, ALIGN_MAP[TYPE_VOIDP])
126
+ @size = addr
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,244 @@
1
+ # frozen_string_literal: false
2
+ require 'fiddle'
3
+ require 'fiddle/value'
4
+ require 'fiddle/pack'
5
+
6
+ module Fiddle
7
+ # C struct shell
8
+ class CStruct
9
+ # accessor to Fiddle::CStructEntity
10
+ def CStruct.entity_class
11
+ CStructEntity
12
+ end
13
+ end
14
+
15
+ # C union shell
16
+ class CUnion
17
+ # accessor to Fiddle::CUnionEntity
18
+ def CUnion.entity_class
19
+ CUnionEntity
20
+ end
21
+ end
22
+
23
+ # Used to construct C classes (CUnion, CStruct, etc)
24
+ #
25
+ # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an
26
+ # easy-to-use manner.
27
+ module CStructBuilder
28
+ # Construct a new class given a C:
29
+ # * class +klass+ (CUnion, CStruct, or other that provide an
30
+ # #entity_class)
31
+ # * +types+ (Fiddle::TYPE_INT, Fiddle::TYPE_SIZE_T, etc., see the C types
32
+ # constants)
33
+ # * corresponding +members+
34
+ #
35
+ # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an
36
+ # easy-to-use manner.
37
+ #
38
+ # Example:
39
+ #
40
+ # require 'fiddle/struct'
41
+ # require 'fiddle/cparser'
42
+ #
43
+ # include Fiddle::CParser
44
+ #
45
+ # types, members = parse_struct_signature(['int i','char c'])
46
+ #
47
+ # MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members)
48
+ #
49
+ # obj = MyStruct.allocate
50
+ #
51
+ def create(klass, types, members)
52
+ new_class = Class.new(klass){
53
+ define_method(:initialize){|addr|
54
+ @entity = klass.entity_class.new(addr, types)
55
+ @entity.assign_names(members)
56
+ }
57
+ define_method(:to_ptr){ @entity }
58
+ define_method(:to_i){ @entity.to_i }
59
+ members.each{|name|
60
+ define_method(name){ @entity[name] }
61
+ define_method(name + "="){|val| @entity[name] = val }
62
+ }
63
+ }
64
+ size = klass.entity_class.size(types)
65
+ new_class.module_eval(<<-EOS, __FILE__, __LINE__+1)
66
+ def new_class.size()
67
+ #{size}
68
+ end
69
+ def new_class.malloc()
70
+ addr = Fiddle.malloc(#{size})
71
+ new(addr)
72
+ end
73
+ EOS
74
+ return new_class
75
+ end
76
+ module_function :create
77
+ end
78
+
79
+ # A C struct wrapper
80
+ class CStructEntity < Fiddle::Pointer
81
+ include PackInfo
82
+ include ValueUtil
83
+
84
+ # Allocates a C struct with the +types+ provided.
85
+ #
86
+ # When the instance is garbage collected, the C function +func+ is called.
87
+ def CStructEntity.malloc(types, func = nil)
88
+ addr = Fiddle.malloc(CStructEntity.size(types))
89
+ CStructEntity.new(addr, types, func)
90
+ end
91
+
92
+ # Returns the offset for the packed sizes for the given +types+.
93
+ #
94
+ # Fiddle::CStructEntity.size(
95
+ # [ Fiddle::TYPE_DOUBLE,
96
+ # Fiddle::TYPE_INT,
97
+ # Fiddle::TYPE_CHAR,
98
+ # Fiddle::TYPE_VOIDP ]) #=> 24
99
+ def CStructEntity.size(types)
100
+ offset = 0
101
+
102
+ max_align = types.map { |type, count = 1|
103
+ last_offset = offset
104
+
105
+ align = PackInfo::ALIGN_MAP[type]
106
+ offset = PackInfo.align(last_offset, align) +
107
+ (PackInfo::SIZE_MAP[type] * count)
108
+
109
+ align
110
+ }.max
111
+
112
+ PackInfo.align(offset, max_align)
113
+ end
114
+
115
+ # Wraps the C pointer +addr+ as a C struct with the given +types+.
116
+ #
117
+ # When the instance is garbage collected, the C function +func+ is called.
118
+ #
119
+ # See also Fiddle::Pointer.new
120
+ def initialize(addr, types, func = nil)
121
+ set_ctypes(types)
122
+ super(addr, @size, func)
123
+ end
124
+
125
+ # Set the names of the +members+ in this C struct
126
+ def assign_names(members)
127
+ @members = members
128
+ end
129
+
130
+ # Calculates the offsets and sizes for the given +types+ in the struct.
131
+ def set_ctypes(types)
132
+ @ctypes = types
133
+ @offset = []
134
+ offset = 0
135
+
136
+ max_align = types.map { |type, count = 1|
137
+ orig_offset = offset
138
+ align = ALIGN_MAP[type]
139
+ offset = PackInfo.align(orig_offset, align)
140
+
141
+ @offset << offset
142
+
143
+ offset += (SIZE_MAP[type] * count)
144
+
145
+ align
146
+ }.max
147
+
148
+ @size = PackInfo.align(offset, max_align)
149
+ end
150
+
151
+ # Fetch struct member +name+
152
+ def [](name)
153
+ idx = @members.index(name)
154
+ if( idx.nil? )
155
+ raise(ArgumentError, "no such member: #{name}")
156
+ end
157
+ ty = @ctypes[idx]
158
+ if( ty.is_a?(Array) )
159
+ r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1])
160
+ else
161
+ r = super(@offset[idx], SIZE_MAP[ty.abs])
162
+ end
163
+ packer = Packer.new([ty])
164
+ val = packer.unpack([r])
165
+ case ty
166
+ when Array
167
+ case ty[0]
168
+ when TYPE_VOIDP
169
+ val = val.collect{|v| Pointer.new(v)}
170
+ end
171
+ when TYPE_VOIDP
172
+ val = Pointer.new(val[0])
173
+ else
174
+ val = val[0]
175
+ end
176
+ if( ty.is_a?(Integer) && (ty < 0) )
177
+ return unsigned_value(val, ty)
178
+ elsif( ty.is_a?(Array) && (ty[0] < 0) )
179
+ return val.collect{|v| unsigned_value(v,ty[0])}
180
+ else
181
+ return val
182
+ end
183
+ end
184
+
185
+ # Set struct member +name+, to value +val+
186
+ def []=(name, val)
187
+ idx = @members.index(name)
188
+ if( idx.nil? )
189
+ raise(ArgumentError, "no such member: #{name}")
190
+ end
191
+ ty = @ctypes[idx]
192
+ packer = Packer.new([ty])
193
+ val = wrap_arg(val, ty, [])
194
+ buff = packer.pack([val].flatten())
195
+ super(@offset[idx], buff.size, buff)
196
+ if( ty.is_a?(Integer) && (ty < 0) )
197
+ return unsigned_value(val, ty)
198
+ elsif( ty.is_a?(Array) && (ty[0] < 0) )
199
+ return val.collect{|v| unsigned_value(v,ty[0])}
200
+ else
201
+ return val
202
+ end
203
+ end
204
+
205
+ def to_s() # :nodoc:
206
+ super(@size)
207
+ end
208
+ end
209
+
210
+ # A C union wrapper
211
+ class CUnionEntity < CStructEntity
212
+ include PackInfo
213
+
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
+ # Returns the size needed for the union with the given +types+.
223
+ #
224
+ # Fiddle::CUnionEntity.size(
225
+ # [ Fiddle::TYPE_DOUBLE,
226
+ # Fiddle::TYPE_INT,
227
+ # Fiddle::TYPE_CHAR,
228
+ # Fiddle::TYPE_VOIDP ]) #=> 8
229
+ def CUnionEntity.size(types)
230
+ types.map { |type, count = 1|
231
+ PackInfo::SIZE_MAP[type] * count
232
+ }.max
233
+ end
234
+
235
+ # Calculate the necessary offset and for each union member with the given
236
+ # +types+
237
+ def set_ctypes(types)
238
+ @ctypes = types
239
+ @offset = Array.new(types.length, 0)
240
+ @size = self.class.size types
241
+ end
242
+ end
243
+ end
244
+
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: false
2
+ module Fiddle
3
+ # Adds Windows type aliases to the including class for use with
4
+ # Fiddle::Importer.
5
+ #
6
+ # The aliases added are:
7
+ # * ATOM
8
+ # * BOOL
9
+ # * BYTE
10
+ # * DWORD
11
+ # * DWORD32
12
+ # * DWORD64
13
+ # * HANDLE
14
+ # * HDC
15
+ # * HINSTANCE
16
+ # * HWND
17
+ # * LPCSTR
18
+ # * LPSTR
19
+ # * PBYTE
20
+ # * PDWORD
21
+ # * PHANDLE
22
+ # * PVOID
23
+ # * PWORD
24
+ # * UCHAR
25
+ # * UINT
26
+ # * ULONG
27
+ # * WORD
28
+ module Win32Types
29
+ def included(m) # :nodoc:
30
+ m.module_eval{
31
+ typealias "DWORD", "unsigned long"
32
+ typealias "PDWORD", "unsigned long *"
33
+ typealias "DWORD32", "unsigned long"
34
+ typealias "DWORD64", "unsigned long long"
35
+ typealias "WORD", "unsigned short"
36
+ typealias "PWORD", "unsigned short *"
37
+ typealias "BOOL", "int"
38
+ typealias "ATOM", "int"
39
+ typealias "BYTE", "unsigned char"
40
+ typealias "PBYTE", "unsigned char *"
41
+ typealias "UINT", "unsigned int"
42
+ typealias "ULONG", "unsigned long"
43
+ typealias "UCHAR", "unsigned char"
44
+ typealias "HANDLE", "uintptr_t"
45
+ typealias "PHANDLE", "void*"
46
+ typealias "PVOID", "void*"
47
+ typealias "LPCSTR", "char*"
48
+ typealias "LPSTR", "char*"
49
+ typealias "HINSTANCE", "unsigned int"
50
+ typealias "HDC", "unsigned int"
51
+ typealias "HWND", "unsigned int"
52
+ }
53
+ end
54
+ module_function :included
55
+ end
56
+
57
+ # Adds basic type aliases to the including class for use with Fiddle::Importer.
58
+ #
59
+ # The aliases added are +uint+ and +u_int+ (<tt>unsigned int</tt>) and
60
+ # +ulong+ and +u_long+ (<tt>unsigned long</tt>)
61
+ module BasicTypes
62
+ def included(m) # :nodoc:
63
+ m.module_eval{
64
+ typealias "uint", "unsigned int"
65
+ typealias "u_int", "unsigned int"
66
+ typealias "ulong", "unsigned long"
67
+ typealias "u_long", "unsigned long"
68
+ }
69
+ end
70
+ module_function :included
71
+ end
72
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: false
2
+ require 'fiddle'
3
+
4
+ module Fiddle
5
+ module ValueUtil #:nodoc: all
6
+ def unsigned_value(val, ty)
7
+ case ty.abs
8
+ when TYPE_CHAR
9
+ [val].pack("c").unpack("C")[0]
10
+ when TYPE_SHORT
11
+ [val].pack("s!").unpack("S!")[0]
12
+ when TYPE_INT
13
+ [val].pack("i!").unpack("I!")[0]
14
+ when TYPE_LONG
15
+ [val].pack("l!").unpack("L!")[0]
16
+ when TYPE_LONG_LONG
17
+ [val].pack("q").unpack("Q")[0]
18
+ else
19
+ val
20
+ end
21
+ end
22
+
23
+ def signed_value(val, ty)
24
+ case ty.abs
25
+ when TYPE_CHAR
26
+ [val].pack("C").unpack("c")[0]
27
+ when TYPE_SHORT
28
+ [val].pack("S!").unpack("s!")[0]
29
+ when TYPE_INT
30
+ [val].pack("I!").unpack("i!")[0]
31
+ when TYPE_LONG
32
+ [val].pack("L!").unpack("l!")[0]
33
+ when TYPE_LONG_LONG
34
+ [val].pack("Q").unpack("q")[0]
35
+ else
36
+ val
37
+ end
38
+ end
39
+
40
+ def wrap_args(args, tys, funcs, &block)
41
+ result = []
42
+ tys ||= []
43
+ args.each_with_index{|arg, idx|
44
+ result.push(wrap_arg(arg, tys[idx], funcs, &block))
45
+ }
46
+ result
47
+ end
48
+
49
+ def wrap_arg(arg, ty, funcs = [], &block)
50
+ funcs ||= []
51
+ case arg
52
+ when nil
53
+ return 0
54
+ when Pointer
55
+ return arg.to_i
56
+ when IO
57
+ case ty
58
+ when TYPE_VOIDP
59
+ return Pointer[arg].to_i
60
+ else
61
+ return arg.to_i
62
+ end
63
+ when Function
64
+ if( block )
65
+ arg.bind_at_call(&block)
66
+ funcs.push(arg)
67
+ elsif !arg.bound?
68
+ raise(RuntimeError, "block must be given.")
69
+ end
70
+ return arg.to_i
71
+ when String
72
+ if( ty.is_a?(Array) )
73
+ return arg.unpack('C*')
74
+ else
75
+ case SIZEOF_VOIDP
76
+ when SIZEOF_LONG
77
+ return [arg].pack("p").unpack("l!")[0]
78
+ when SIZEOF_LONG_LONG
79
+ return [arg].pack("p").unpack("q")[0]
80
+ else
81
+ raise(RuntimeError, "sizeof(void*)?")
82
+ end
83
+ end
84
+ when Float, Integer
85
+ return arg
86
+ when Array
87
+ if( ty.is_a?(Array) ) # used only by struct
88
+ case ty[0]
89
+ when TYPE_VOIDP
90
+ return arg.collect{|v| Integer(v)}
91
+ when TYPE_CHAR
92
+ if( arg.is_a?(String) )
93
+ return val.unpack('C*')
94
+ end
95
+ end
96
+ return arg
97
+ else
98
+ return arg
99
+ end
100
+ else
101
+ if( arg.respond_to?(:to_ptr) )
102
+ return arg.to_ptr.to_i
103
+ else
104
+ begin
105
+ return Integer(arg)
106
+ rescue
107
+ raise(ArgumentError, "unknown argument type: #{arg.class}")
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end