fiddle 1.0.0.beta2 → 1.0.4
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 +5 -5
- data/README.md +72 -3
- data/Rakefile +4 -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 +205 -16
- data/ext/fiddle/conversions.h +14 -4
- data/ext/fiddle/depend +85 -0
- data/ext/fiddle/extconf.rb +60 -29
- data/ext/fiddle/extlibs +10 -2
- data/ext/fiddle/fiddle.c +133 -34
- data/ext/fiddle/fiddle.h +96 -32
- data/ext/fiddle/function.c +261 -93
- data/ext/fiddle/handle.c +10 -12
- data/ext/fiddle/memory_view.c +254 -0
- data/ext/fiddle/pinned.c +123 -0
- data/ext/fiddle/pointer.c +147 -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 +50 -5
- data/lib/fiddle.rb +3 -1
- data/lib/fiddle/cparser.rb +93 -25
- data/lib/fiddle/function.rb +5 -0
- 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 +18 -15
- data/.gitignore +0 -13
- data/.travis.yml +0 -5
- data/Gemfile +0 -4
- data/bin/console +0 -14
- data/bin/setup +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b7e244560b492c43b7458506409f4d624372766d882e306a48a08fc9ad32671b
|
4
|
+
data.tar.gz: 9a166eb1dded473780e8ea89d1240da28ff6be73e72f2026eff14f6e55f060b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c4509d95af60a2f16047e431580a3e6b1828b0bcf0820302d678c1b2f8e9462df767ce5a6c7e60ef728fa0d7a3afe667cbbe982e161802dafc49ed0a398bf2a
|
7
|
+
data.tar.gz: 00623b0e514fad355b76ec9d59273c15759d5991c2832c6df10c23ad06ef98f628d5d6381cfd75449ee0c2811b3f58d66b55824993c5d3aef1a2d2be1d79a8de
|
data/README.md
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
# Fiddle
|
2
2
|
|
3
|
-
|
3
|
+
[](https://travis-ci.org/ruby/fiddle)
|
4
4
|
|
5
|
-
|
5
|
+
A libffi wrapper for Ruby.
|
6
|
+
|
7
|
+
Fiddle is an extension to translate a foreign function interface (FFI) with ruby.
|
8
|
+
|
9
|
+
It wraps [libffi](http://sourceware.org/libffi/), a popular C library which provides a portable interface that allows code written in one language to call code written in another language.
|
6
10
|
|
7
11
|
## Installation
|
8
12
|
|
@@ -22,7 +26,72 @@ Or install it yourself as:
|
|
22
26
|
|
23
27
|
## Usage
|
24
28
|
|
25
|
-
|
29
|
+
Here we will use Fiddle::Function to wrap [floor(3) from libm](http://linux.die.net/man/3/floor)
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require 'fiddle'
|
33
|
+
|
34
|
+
libm = Fiddle.dlopen('/lib/libm.so.6')
|
35
|
+
|
36
|
+
floor = Fiddle::Function.new(
|
37
|
+
libm['floor'],
|
38
|
+
[Fiddle::TYPE_DOUBLE],
|
39
|
+
Fiddle::TYPE_DOUBLE
|
40
|
+
)
|
41
|
+
|
42
|
+
puts floor.call(3.14159) #=> 3.0
|
43
|
+
```
|
44
|
+
|
45
|
+
### Nested Structs
|
46
|
+
|
47
|
+
You can use hashes to create nested structs, where the hash keys are member names and the values are the nested structs:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
StudentCollegeDetail = struct [
|
51
|
+
'int college_id',
|
52
|
+
'char college_name[50]'
|
53
|
+
]
|
54
|
+
|
55
|
+
StudentDetail = struct [
|
56
|
+
'int id',
|
57
|
+
'char name[20]',
|
58
|
+
{ clg_data: StudentCollegeDetail }
|
59
|
+
]
|
60
|
+
```
|
61
|
+
|
62
|
+
You can also specify an anonymous nested struct, like so:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
StudentDetail = struct [
|
66
|
+
'int id',
|
67
|
+
'char name[20]',
|
68
|
+
{
|
69
|
+
clg_data: struct([
|
70
|
+
'int college_id',
|
71
|
+
'char college_name[50]'
|
72
|
+
])
|
73
|
+
}
|
74
|
+
]
|
75
|
+
```
|
76
|
+
|
77
|
+
The position of a hash (and the order of the keys in the hash, in the case of a hash with multiple entries), dictate the offsets of the nested struct in memory. The following examples are both syntactically valid but will lay out the structs differently in memory:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
# order of members in memory: position, id, dimensions
|
81
|
+
Rect = struct [ { position: struct(['float x', 'float y']) },
|
82
|
+
'int id',
|
83
|
+
{ dimensions: struct(['float w', 'float h']) }
|
84
|
+
]
|
85
|
+
|
86
|
+
# order of members in memory: id, position, dimensions
|
87
|
+
Rect = struct [ 'int id',
|
88
|
+
{
|
89
|
+
position: struct(['float x', 'float y']),
|
90
|
+
dimensions: struct(['float w', 'float h'])
|
91
|
+
}
|
92
|
+
]
|
93
|
+
```
|
94
|
+
|
26
95
|
|
27
96
|
## Development
|
28
97
|
|
data/Rakefile
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
-
require "rake/testtask"
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
t.test_files = FileList['test/**/test_*.rb']
|
3
|
+
desc "Run test"
|
4
|
+
task :test do
|
5
|
+
ruby("test/run.rb")
|
8
6
|
end
|
9
7
|
|
10
8
|
require 'rake/extensiontask'
|
11
9
|
Rake::ExtensionTask.new("fiddle")
|
10
|
+
Rake::ExtensionTask.new("-test-/memory_view")
|
12
11
|
|
13
12
|
task :default => [:compile, :test]
|
data/bin/downloader.rb
ADDED
@@ -0,0 +1,331 @@
|
|
1
|
+
# Used by configure and make to download or update mirrored Ruby and GCC
|
2
|
+
# files. This will use HTTPS if possible, falling back to HTTP.
|
3
|
+
|
4
|
+
require 'fileutils'
|
5
|
+
require 'open-uri'
|
6
|
+
require 'pathname'
|
7
|
+
begin
|
8
|
+
require 'net/https'
|
9
|
+
rescue LoadError
|
10
|
+
https = 'http'
|
11
|
+
else
|
12
|
+
https = 'https'
|
13
|
+
|
14
|
+
# open-uri of ruby 2.2.0 accept an array of PEMs as ssl_ca_cert, but old
|
15
|
+
# versions are not. so, patching OpenSSL::X509::Store#add_file instead.
|
16
|
+
class OpenSSL::X509::Store
|
17
|
+
alias orig_add_file add_file
|
18
|
+
def add_file(pems)
|
19
|
+
Array(pems).each do |pem|
|
20
|
+
if File.directory?(pem)
|
21
|
+
add_path pem
|
22
|
+
else
|
23
|
+
orig_add_file pem
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
# since open-uri internally checks ssl_ca_cert using File.directory?,
|
29
|
+
# allow to accept an array.
|
30
|
+
class <<File
|
31
|
+
alias orig_directory? directory?
|
32
|
+
def File.directory? files
|
33
|
+
files.is_a?(Array) ? false : orig_directory?(files)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Downloader
|
39
|
+
def self.https=(https)
|
40
|
+
@@https = https
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.https?
|
44
|
+
@@https == 'https'
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.https
|
48
|
+
@@https
|
49
|
+
end
|
50
|
+
|
51
|
+
class GNU < self
|
52
|
+
def self.download(name, *rest)
|
53
|
+
if https?
|
54
|
+
super("https://raw.githubusercontent.com/gcc-mirror/gcc/master/#{name}", name, *rest)
|
55
|
+
else
|
56
|
+
super("https://repo.or.cz/official-gcc.git/blob_plain/HEAD:/#{name}", name, *rest)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class RubyGems < self
|
62
|
+
def self.download(name, dir = nil, since = true, options = {})
|
63
|
+
require 'rubygems'
|
64
|
+
options = options.dup
|
65
|
+
options[:ssl_ca_cert] = Dir.glob(File.expand_path("../lib/rubygems/ssl_certs/**/*.pem", File.dirname(__FILE__)))
|
66
|
+
super("https://rubygems.org/downloads/#{name}", name, dir, since, options)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
Gems = RubyGems
|
71
|
+
|
72
|
+
class Unicode < self
|
73
|
+
def self.download(name, *rest)
|
74
|
+
super("http://www.unicode.org/Public/#{name}", name, *rest)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.mode_for(data)
|
79
|
+
/\A#!/ =~ data ? 0755 : 0644
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.http_options(file, since)
|
83
|
+
options = {}
|
84
|
+
if since
|
85
|
+
case since
|
86
|
+
when true
|
87
|
+
since = (File.mtime(file).httpdate rescue nil)
|
88
|
+
when Time
|
89
|
+
since = since.httpdate
|
90
|
+
end
|
91
|
+
if since
|
92
|
+
options['If-Modified-Since'] = since
|
93
|
+
end
|
94
|
+
end
|
95
|
+
options['Accept-Encoding'] = '*' # to disable Net::HTTP::GenericRequest#decode_content
|
96
|
+
options
|
97
|
+
end
|
98
|
+
|
99
|
+
# Downloader.download(url, name, [dir, [since]])
|
100
|
+
#
|
101
|
+
# Update a file from url if newer version is available.
|
102
|
+
# Creates the file if the file doesn't yet exist; however, the
|
103
|
+
# directory where the file is being created has to exist already.
|
104
|
+
# The +since+ parameter can take the following values, with associated meanings:
|
105
|
+
# true ::
|
106
|
+
# Take the last-modified time of the current file on disk, and only download
|
107
|
+
# if the server has a file that was modified later. Download unconditionally
|
108
|
+
# if we don't have the file yet. Default.
|
109
|
+
# +some time value+ ::
|
110
|
+
# Use this time value instead of the time of modification of the file on disk.
|
111
|
+
# nil ::
|
112
|
+
# Only download the file if it doesn't exist yet.
|
113
|
+
# false ::
|
114
|
+
# always download url regardless of whether we already have a file,
|
115
|
+
# and regardless of modification times. (This is essentially just a waste of
|
116
|
+
# network resources, except in the case that the file we have is somehow damaged.
|
117
|
+
# Please note that using this recurringly might create or be seen as a
|
118
|
+
# denial of service attack.)
|
119
|
+
#
|
120
|
+
# Example usage:
|
121
|
+
# download 'http://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt',
|
122
|
+
# 'UnicodeData.txt', 'enc/unicode/data'
|
123
|
+
def self.download(url, name, dir = nil, since = true, options = {})
|
124
|
+
options = options.dup
|
125
|
+
url = URI(url)
|
126
|
+
dryrun = options.delete(:dryrun)
|
127
|
+
if name
|
128
|
+
file = Pathname.new(under(dir, name))
|
129
|
+
else
|
130
|
+
name = File.basename(url.path)
|
131
|
+
end
|
132
|
+
cache_save = options.delete(:cache_save) {
|
133
|
+
ENV["CACHE_SAVE"] != "no"
|
134
|
+
}
|
135
|
+
cache = cache_file(url, name, options.delete(:cache_dir))
|
136
|
+
file ||= cache
|
137
|
+
if since.nil? and file.exist?
|
138
|
+
if $VERBOSE
|
139
|
+
$stdout.puts "#{file} already exists"
|
140
|
+
$stdout.flush
|
141
|
+
end
|
142
|
+
if cache_save
|
143
|
+
save_cache(cache, file, name)
|
144
|
+
end
|
145
|
+
return file.to_path
|
146
|
+
end
|
147
|
+
if dryrun
|
148
|
+
puts "Download #{url} into #{file}"
|
149
|
+
return
|
150
|
+
end
|
151
|
+
if link_cache(cache, file, name, $VERBOSE)
|
152
|
+
return file.to_path
|
153
|
+
end
|
154
|
+
if !https? and URI::HTTPS === url
|
155
|
+
warn "*** using http instead of https ***"
|
156
|
+
url.scheme = 'http'
|
157
|
+
url = URI(url.to_s)
|
158
|
+
end
|
159
|
+
if $VERBOSE
|
160
|
+
$stdout.print "downloading #{name} ... "
|
161
|
+
$stdout.flush
|
162
|
+
end
|
163
|
+
begin
|
164
|
+
data = url.read(options.merge(http_options(file, since.nil? ? true : since)))
|
165
|
+
rescue OpenURI::HTTPError => http_error
|
166
|
+
if http_error.message =~ /^304 / # 304 Not Modified
|
167
|
+
if $VERBOSE
|
168
|
+
$stdout.puts "#{name} not modified"
|
169
|
+
$stdout.flush
|
170
|
+
end
|
171
|
+
return file.to_path
|
172
|
+
end
|
173
|
+
raise
|
174
|
+
rescue Timeout::Error
|
175
|
+
if since.nil? and file.exist?
|
176
|
+
puts "Request for #{url} timed out, using old version."
|
177
|
+
return file.to_path
|
178
|
+
end
|
179
|
+
raise
|
180
|
+
rescue SocketError
|
181
|
+
if since.nil? and file.exist?
|
182
|
+
puts "No network connection, unable to download #{url}, using old version."
|
183
|
+
return file.to_path
|
184
|
+
end
|
185
|
+
raise
|
186
|
+
end
|
187
|
+
mtime = nil
|
188
|
+
dest = (cache_save && cache && !cache.exist? ? cache : file)
|
189
|
+
dest.parent.mkpath
|
190
|
+
dest.open("wb", 0600) do |f|
|
191
|
+
f.write(data)
|
192
|
+
f.chmod(mode_for(data))
|
193
|
+
mtime = data.meta["last-modified"]
|
194
|
+
end
|
195
|
+
if mtime
|
196
|
+
mtime = Time.httpdate(mtime)
|
197
|
+
dest.utime(mtime, mtime)
|
198
|
+
end
|
199
|
+
if $VERBOSE
|
200
|
+
$stdout.puts "done"
|
201
|
+
$stdout.flush
|
202
|
+
end
|
203
|
+
if dest.eql?(cache)
|
204
|
+
link_cache(cache, file, name)
|
205
|
+
elsif cache_save
|
206
|
+
save_cache(cache, file, name)
|
207
|
+
end
|
208
|
+
return file.to_path
|
209
|
+
rescue => e
|
210
|
+
raise "failed to download #{name}\n#{e.message}: #{url}"
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.under(dir, name)
|
214
|
+
dir ? File.join(dir, File.basename(name)) : name
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.cache_file(url, name, cache_dir = nil)
|
218
|
+
case cache_dir
|
219
|
+
when false
|
220
|
+
return nil
|
221
|
+
when nil
|
222
|
+
cache_dir = ENV['CACHE_DIR']
|
223
|
+
if !cache_dir or cache_dir.empty?
|
224
|
+
cache_dir = ".downloaded-cache"
|
225
|
+
end
|
226
|
+
end
|
227
|
+
Pathname.new(cache_dir) + (name || File.basename(URI(url).path))
|
228
|
+
end
|
229
|
+
|
230
|
+
def self.link_cache(cache, file, name, verbose = false)
|
231
|
+
return false unless cache and cache.exist?
|
232
|
+
return true if cache.eql?(file)
|
233
|
+
if /cygwin/ !~ RUBY_PLATFORM or /winsymlink:nativestrict/ =~ ENV['CYGWIN']
|
234
|
+
begin
|
235
|
+
file.make_symlink(cache.relative_path_from(file.parent))
|
236
|
+
rescue SystemCallError
|
237
|
+
else
|
238
|
+
if verbose
|
239
|
+
$stdout.puts "made symlink #{name} to #{cache}"
|
240
|
+
$stdout.flush
|
241
|
+
end
|
242
|
+
return true
|
243
|
+
end
|
244
|
+
end
|
245
|
+
begin
|
246
|
+
file.make_link(cache)
|
247
|
+
rescue SystemCallError
|
248
|
+
else
|
249
|
+
if verbose
|
250
|
+
$stdout.puts "made link #{name} to #{cache}"
|
251
|
+
$stdout.flush
|
252
|
+
end
|
253
|
+
return true
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def self.save_cache(cache, file, name)
|
258
|
+
if cache and !cache.eql?(file) and !cache.exist?
|
259
|
+
begin
|
260
|
+
file.rename(cache)
|
261
|
+
rescue
|
262
|
+
else
|
263
|
+
link_cache(cache, file, name)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
Downloader.https = https.freeze
|
270
|
+
|
271
|
+
if $0 == __FILE__
|
272
|
+
since = true
|
273
|
+
options = {}
|
274
|
+
until ARGV.empty?
|
275
|
+
case ARGV[0]
|
276
|
+
when '-d'
|
277
|
+
destdir = ARGV[1]
|
278
|
+
ARGV.shift
|
279
|
+
when '-p'
|
280
|
+
# strip directory names from the name to download, and add the
|
281
|
+
# prefix instead.
|
282
|
+
prefix = ARGV[1]
|
283
|
+
ARGV.shift
|
284
|
+
when '-e'
|
285
|
+
since = nil
|
286
|
+
when '-a'
|
287
|
+
since = false
|
288
|
+
when '-n', '--dryrun'
|
289
|
+
options[:dryrun] = true
|
290
|
+
when '--cache-dir'
|
291
|
+
options[:cache_dir] = ARGV[1]
|
292
|
+
ARGV.shift
|
293
|
+
when /\A--cache-dir=(.*)/m
|
294
|
+
options[:cache_dir] = $1
|
295
|
+
when /\A-/
|
296
|
+
abort "#{$0}: unknown option #{ARGV[0]}"
|
297
|
+
else
|
298
|
+
break
|
299
|
+
end
|
300
|
+
ARGV.shift
|
301
|
+
end
|
302
|
+
dl = Downloader.constants.find do |name|
|
303
|
+
ARGV[0].casecmp(name.to_s) == 0
|
304
|
+
end unless ARGV.empty?
|
305
|
+
$VERBOSE = true
|
306
|
+
if dl
|
307
|
+
dl = Downloader.const_get(dl)
|
308
|
+
ARGV.shift
|
309
|
+
ARGV.each do |name|
|
310
|
+
dir = destdir
|
311
|
+
if prefix
|
312
|
+
name = name.sub(/\A\.\//, '')
|
313
|
+
if name.start_with?(destdir+"/")
|
314
|
+
name = name[(destdir.size+1)..-1]
|
315
|
+
if (dir = File.dirname(name)) == '.'
|
316
|
+
dir = destdir
|
317
|
+
else
|
318
|
+
dir = File.join(destdir, dir)
|
319
|
+
end
|
320
|
+
else
|
321
|
+
name = File.basename(name)
|
322
|
+
end
|
323
|
+
name = "#{prefix}/#{name}"
|
324
|
+
end
|
325
|
+
dl.download(name, dir, since, options)
|
326
|
+
end
|
327
|
+
else
|
328
|
+
abort "usage: #{$0} url name" unless ARGV.size == 2
|
329
|
+
Downloader.download(ARGV[0], ARGV[1], destdir, since, options)
|
330
|
+
end
|
331
|
+
end
|