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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2fe43921484c5d31435a574dfefe01f997a7d32d
4
+ data.tar.gz: e03146994af328004ad234e857040db4820f3734
5
+ SHA512:
6
+ metadata.gz: a5ba7ff66c1000fbc80c61294523e9acba69f39bdce1f168dd748821a1fa2213204b69a3174ab74274e1343f936a8fe0a78053d4b43c6736ac7232e38e6a70d8
7
+ data.tar.gz: b650a3bb827441f5a34e51e66df247c8c707383de10083f717a15500fffa020d1f5704b53558ea1b5338a3481fa968a17f2ddfa63ec51ab5b3b7c1f3fbf5a052
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.dll
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ before_install: gem install bundler -v 1.14.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fiddle.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 SHIBATA Hiroshi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,41 @@
1
+ # Fiddle
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/fiddle`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'fiddle'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install fiddle
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/hsbt/fiddle.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
+
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test" << "test/lib"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/test_*.rb']
8
+ end
9
+
10
+ require 'rake/extensiontask'
11
+ Rake::ExtensionTask.new("fiddle")
12
+
13
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "fiddle"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,345 @@
1
+ #include <fiddle.h>
2
+ #include <ruby/thread.h>
3
+
4
+ int ruby_thread_has_gvl_p(void); /* for ext/fiddle/closure.c */
5
+
6
+ VALUE cFiddleClosure;
7
+
8
+ typedef struct {
9
+ void * code;
10
+ ffi_closure *pcl;
11
+ ffi_cif cif;
12
+ int argc;
13
+ ffi_type **argv;
14
+ } fiddle_closure;
15
+
16
+ #if defined(USE_FFI_CLOSURE_ALLOC)
17
+ #elif defined(__OpenBSD__) || defined(__APPLE__) || defined(__linux__)
18
+ # define USE_FFI_CLOSURE_ALLOC 0
19
+ #elif defined(RUBY_LIBFFI_MODVERSION) && RUBY_LIBFFI_MODVERSION < 3000005 && \
20
+ (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_AMD64))
21
+ # define USE_FFI_CLOSURE_ALLOC 0
22
+ #else
23
+ # define USE_FFI_CLOSURE_ALLOC 1
24
+ #endif
25
+
26
+ static void
27
+ dealloc(void * ptr)
28
+ {
29
+ fiddle_closure * cls = (fiddle_closure *)ptr;
30
+ #if USE_FFI_CLOSURE_ALLOC
31
+ ffi_closure_free(cls->pcl);
32
+ #else
33
+ munmap(cls->pcl, sizeof(*cls->pcl));
34
+ #endif
35
+ if (cls->argv) xfree(cls->argv);
36
+ xfree(cls);
37
+ }
38
+
39
+ static size_t
40
+ closure_memsize(const void * ptr)
41
+ {
42
+ fiddle_closure * cls = (fiddle_closure *)ptr;
43
+ size_t size = 0;
44
+
45
+ size += sizeof(*cls);
46
+ #if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
47
+ size += ffi_raw_size(&cls->cif);
48
+ #endif
49
+ size += sizeof(*cls->argv);
50
+ size += sizeof(ffi_closure);
51
+
52
+ return size;
53
+ }
54
+
55
+ const rb_data_type_t closure_data_type = {
56
+ "fiddle/closure",
57
+ {0, dealloc, closure_memsize,},
58
+ };
59
+
60
+ struct callback_args {
61
+ ffi_cif *cif;
62
+ void *resp;
63
+ void **args;
64
+ void *ctx;
65
+ };
66
+
67
+ static void *
68
+ with_gvl_callback(void *ptr)
69
+ {
70
+ struct callback_args *x = ptr;
71
+
72
+ VALUE self = (VALUE)x->ctx;
73
+ VALUE rbargs = rb_iv_get(self, "@args");
74
+ VALUE ctype = rb_iv_get(self, "@ctype");
75
+ int argc = RARRAY_LENINT(rbargs);
76
+ VALUE params = rb_ary_tmp_new(argc);
77
+ VALUE ret;
78
+ VALUE cPointer;
79
+ int i, type;
80
+
81
+ cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
82
+
83
+ for (i = 0; i < argc; i++) {
84
+ type = NUM2INT(RARRAY_AREF(rbargs, i));
85
+ switch (type) {
86
+ case TYPE_VOID:
87
+ argc = 0;
88
+ break;
89
+ case TYPE_INT:
90
+ rb_ary_push(params, INT2NUM(*(int *)x->args[i]));
91
+ break;
92
+ case -TYPE_INT:
93
+ rb_ary_push(params, UINT2NUM(*(unsigned int *)x->args[i]));
94
+ break;
95
+ case TYPE_VOIDP:
96
+ rb_ary_push(params,
97
+ rb_funcall(cPointer, rb_intern("[]"), 1,
98
+ PTR2NUM(*(void **)x->args[i])));
99
+ break;
100
+ case TYPE_LONG:
101
+ rb_ary_push(params, LONG2NUM(*(long *)x->args[i]));
102
+ break;
103
+ case -TYPE_LONG:
104
+ rb_ary_push(params, ULONG2NUM(*(unsigned long *)x->args[i]));
105
+ break;
106
+ case TYPE_CHAR:
107
+ rb_ary_push(params, INT2NUM(*(signed char *)x->args[i]));
108
+ break;
109
+ case -TYPE_CHAR:
110
+ rb_ary_push(params, UINT2NUM(*(unsigned char *)x->args[i]));
111
+ break;
112
+ case TYPE_SHORT:
113
+ rb_ary_push(params, INT2NUM(*(signed short *)x->args[i]));
114
+ break;
115
+ case -TYPE_SHORT:
116
+ rb_ary_push(params, UINT2NUM(*(unsigned short *)x->args[i]));
117
+ break;
118
+ case TYPE_DOUBLE:
119
+ rb_ary_push(params, rb_float_new(*(double *)x->args[i]));
120
+ break;
121
+ case TYPE_FLOAT:
122
+ rb_ary_push(params, rb_float_new(*(float *)x->args[i]));
123
+ break;
124
+ #if HAVE_LONG_LONG
125
+ case TYPE_LONG_LONG:
126
+ rb_ary_push(params, LL2NUM(*(LONG_LONG *)x->args[i]));
127
+ break;
128
+ case -TYPE_LONG_LONG:
129
+ rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)x->args[i]));
130
+ break;
131
+ #endif
132
+ default:
133
+ rb_raise(rb_eRuntimeError, "closure args: %d", type);
134
+ }
135
+ }
136
+
137
+ ret = rb_funcall2(self, rb_intern("call"), argc, RARRAY_CONST_PTR(params));
138
+ RB_GC_GUARD(params);
139
+
140
+ type = NUM2INT(ctype);
141
+ switch (type) {
142
+ case TYPE_VOID:
143
+ break;
144
+ case TYPE_LONG:
145
+ *(long *)x->resp = NUM2LONG(ret);
146
+ break;
147
+ case -TYPE_LONG:
148
+ *(unsigned long *)x->resp = NUM2ULONG(ret);
149
+ break;
150
+ case TYPE_CHAR:
151
+ case TYPE_SHORT:
152
+ case TYPE_INT:
153
+ *(ffi_sarg *)x->resp = NUM2INT(ret);
154
+ break;
155
+ case -TYPE_CHAR:
156
+ case -TYPE_SHORT:
157
+ case -TYPE_INT:
158
+ *(ffi_arg *)x->resp = NUM2UINT(ret);
159
+ break;
160
+ case TYPE_VOIDP:
161
+ *(void **)x->resp = NUM2PTR(ret);
162
+ break;
163
+ case TYPE_DOUBLE:
164
+ *(double *)x->resp = NUM2DBL(ret);
165
+ break;
166
+ case TYPE_FLOAT:
167
+ *(float *)x->resp = (float)NUM2DBL(ret);
168
+ break;
169
+ #if HAVE_LONG_LONG
170
+ case TYPE_LONG_LONG:
171
+ *(LONG_LONG *)x->resp = NUM2LL(ret);
172
+ break;
173
+ case -TYPE_LONG_LONG:
174
+ *(unsigned LONG_LONG *)x->resp = NUM2ULL(ret);
175
+ break;
176
+ #endif
177
+ default:
178
+ rb_raise(rb_eRuntimeError, "closure retval: %d", type);
179
+ }
180
+ return 0;
181
+ }
182
+
183
+ static void
184
+ callback(ffi_cif *cif, void *resp, void **args, void *ctx)
185
+ {
186
+ struct callback_args x;
187
+
188
+ x.cif = cif;
189
+ x.resp = resp;
190
+ x.args = args;
191
+ x.ctx = ctx;
192
+
193
+ if (ruby_thread_has_gvl_p()) {
194
+ (void)with_gvl_callback(&x);
195
+ } else {
196
+ (void)rb_thread_call_with_gvl(with_gvl_callback, &x);
197
+ }
198
+ }
199
+
200
+ static VALUE
201
+ allocate(VALUE klass)
202
+ {
203
+ fiddle_closure * closure;
204
+
205
+ VALUE i = TypedData_Make_Struct(klass, fiddle_closure,
206
+ &closure_data_type, closure);
207
+
208
+ #if USE_FFI_CLOSURE_ALLOC
209
+ closure->pcl = ffi_closure_alloc(sizeof(ffi_closure), &closure->code);
210
+ #else
211
+ closure->pcl = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE,
212
+ MAP_ANON | MAP_PRIVATE, -1, 0);
213
+ #endif
214
+
215
+ return i;
216
+ }
217
+
218
+ static VALUE
219
+ initialize(int rbargc, VALUE argv[], VALUE self)
220
+ {
221
+ VALUE ret;
222
+ VALUE args;
223
+ VALUE abi;
224
+ fiddle_closure * cl;
225
+ ffi_cif * cif;
226
+ ffi_closure *pcl;
227
+ ffi_status result;
228
+ int i, argc;
229
+
230
+ if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
231
+ abi = INT2NUM(FFI_DEFAULT_ABI);
232
+
233
+ Check_Type(args, T_ARRAY);
234
+
235
+ argc = RARRAY_LENINT(args);
236
+
237
+ TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
238
+
239
+ cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
240
+
241
+ for (i = 0; i < argc; i++) {
242
+ int type = NUM2INT(RARRAY_AREF(args, i));
243
+ cl->argv[i] = INT2FFI_TYPE(type);
244
+ }
245
+ cl->argv[argc] = NULL;
246
+
247
+ rb_iv_set(self, "@ctype", ret);
248
+ rb_iv_set(self, "@args", args);
249
+
250
+ cif = &cl->cif;
251
+ pcl = cl->pcl;
252
+
253
+ result = ffi_prep_cif(cif, NUM2INT(abi), argc,
254
+ INT2FFI_TYPE(NUM2INT(ret)),
255
+ cl->argv);
256
+
257
+ if (FFI_OK != result)
258
+ rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
259
+
260
+ #if USE_FFI_CLOSURE_ALLOC
261
+ result = ffi_prep_closure_loc(pcl, cif, callback,
262
+ (void *)self, cl->code);
263
+ #else
264
+ result = ffi_prep_closure(pcl, cif, callback, (void *)self);
265
+ cl->code = (void *)pcl;
266
+ i = mprotect(pcl, sizeof(*pcl), PROT_READ | PROT_EXEC);
267
+ if (i) {
268
+ rb_sys_fail("mprotect");
269
+ }
270
+ #endif
271
+
272
+ if (FFI_OK != result)
273
+ rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
274
+
275
+ return self;
276
+ }
277
+
278
+ static VALUE
279
+ to_i(VALUE self)
280
+ {
281
+ fiddle_closure * cl;
282
+ void *code;
283
+
284
+ TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
285
+
286
+ code = cl->code;
287
+
288
+ return PTR2NUM(code);
289
+ }
290
+
291
+ void
292
+ Init_fiddle_closure(void)
293
+ {
294
+ #if 0
295
+ mFiddle = rb_define_module("Fiddle"); /* let rdoc know about mFiddle */
296
+ #endif
297
+
298
+ /*
299
+ * Document-class: Fiddle::Closure
300
+ *
301
+ * == Description
302
+ *
303
+ * An FFI closure wrapper, for handling callbacks.
304
+ *
305
+ * == Example
306
+ *
307
+ * closure = Class.new(Fiddle::Closure) {
308
+ * def call
309
+ * 10
310
+ * end
311
+ * }.new(Fiddle::TYPE_INT, [])
312
+ * #=> #<#<Class:0x0000000150d308>:0x0000000150d240>
313
+ * func = Fiddle::Function.new(closure, [], Fiddle::TYPE_INT)
314
+ * #=> #<Fiddle::Function:0x00000001516e58>
315
+ * func.call
316
+ * #=> 10
317
+ */
318
+ cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject);
319
+
320
+ rb_define_alloc_func(cFiddleClosure, allocate);
321
+
322
+ /*
323
+ * Document-method: new
324
+ *
325
+ * call-seq: new(ret, args, abi = Fiddle::DEFAULT)
326
+ *
327
+ * Construct a new Closure object.
328
+ *
329
+ * * +ret+ is the C type to be returned
330
+ * * +args+ is an Array of arguments, passed to the callback function
331
+ * * +abi+ is the abi of the closure
332
+ *
333
+ * If there is an error in preparing the ffi_cif or ffi_prep_closure,
334
+ * then a RuntimeError will be raised.
335
+ */
336
+ rb_define_method(cFiddleClosure, "initialize", initialize, -1);
337
+
338
+ /*
339
+ * Document-method: to_i
340
+ *
341
+ * Returns the memory address for this closure
342
+ */
343
+ rb_define_method(cFiddleClosure, "to_i", to_i, 0);
344
+ }
345
+ /* vim: set noet sw=4 sts=4 */