fiddle 1.0.0.beta1

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.
@@ -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