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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 1c95f026d86d58a824f9925f5a57d8cebbf00e0a
4
- data.tar.gz: ec3332b0ef7aadc1e47bf5d563e61232410395f6
2
+ SHA256:
3
+ metadata.gz: b7e244560b492c43b7458506409f4d624372766d882e306a48a08fc9ad32671b
4
+ data.tar.gz: 9a166eb1dded473780e8ea89d1240da28ff6be73e72f2026eff14f6e55f060b9
5
5
  SHA512:
6
- metadata.gz: 7173bf1fa4282f82118ce7dbe0064b170dc0c21ba6d99fbb7e52e356a6ad9710ba2c47e2225e56a2b9052358a8a7763228f302fc3ffbff97555fc2eca9fe6115
7
- data.tar.gz: a271e17bc2a5626e05fff933991829d377333325269a3d4daec33fc70bb524d1d2c1ab36bf749b0e27e17aec5949313ec51aab7dc9c16215fed753d988415f58
6
+ metadata.gz: 7c4509d95af60a2f16047e431580a3e6b1828b0bcf0820302d678c1b2f8e9462df767ce5a6c7e60ef728fa0d7a3afe667cbbe982e161802dafc49ed0a398bf2a
7
+ data.tar.gz: 00623b0e514fad355b76ec9d59273c15759d5991c2832c6df10c23ad06ef98f628d5d6381cfd75449ee0c2811b3f58d66b55824993c5d3aef1a2d2be1d79a8de
data/README.md CHANGED
@@ -1,8 +1,12 @@
1
1
  # Fiddle
2
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.
3
+ [![Build Status](https://travis-ci.org/ruby/fiddle.svg?branch=master)](https://travis-ci.org/ruby/fiddle)
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
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
- TODO: Write usage instructions here
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
- Rake::TestTask.new(:test) do |t|
5
- t.libs << "test" << "test/lib"
6
- t.libs << "lib"
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]
@@ -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