fiddle 1.0.9 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/fiddle/struct.rb CHANGED
@@ -13,6 +13,58 @@ module Fiddle
13
13
  CStructEntity
14
14
  end
15
15
 
16
+ def self.offsetof(name, members, types) # :nodoc:
17
+ offset = 0
18
+ worklist = name.split('.')
19
+ this_type = self
20
+ while search_name = worklist.shift
21
+ index = 0
22
+ member_index = members.index(search_name)
23
+
24
+ unless member_index
25
+ # Possibly a sub-structure
26
+ member_index = members.index { |member_name, _|
27
+ member_name == search_name
28
+ }
29
+ return unless member_index
30
+ end
31
+
32
+ types.each { |type, count = 1|
33
+ orig_offset = offset
34
+ if type.respond_to?(:entity_class)
35
+ align = type.alignment
36
+ type_size = type.size
37
+ else
38
+ align = PackInfo::ALIGN_MAP[type]
39
+ type_size = PackInfo::SIZE_MAP[type]
40
+ end
41
+
42
+ # Unions shouldn't advance the offset
43
+ if this_type.entity_class == CUnionEntity
44
+ type_size = 0
45
+ end
46
+
47
+ offset = PackInfo.align(orig_offset, align)
48
+
49
+ if worklist.empty?
50
+ return offset if index == member_index
51
+ else
52
+ if index == member_index
53
+ subtype = types[member_index]
54
+ members = subtype.members
55
+ types = subtype.types
56
+ this_type = subtype
57
+ break
58
+ end
59
+ end
60
+
61
+ offset += (type_size * count)
62
+ index += 1
63
+ }
64
+ end
65
+ nil
66
+ end
67
+
16
68
  def each
17
69
  return enum_for(__function__) unless block_given?
18
70
 
@@ -75,6 +127,10 @@ module Fiddle
75
127
  def CUnion.entity_class
76
128
  CUnionEntity
77
129
  end
130
+
131
+ def self.offsetof(name, members, types) # :nodoc:
132
+ 0
133
+ end
78
134
  end
79
135
 
80
136
  # Wrapper for arrays within a struct
@@ -172,6 +228,21 @@ module Fiddle
172
228
  define_method(:to_i){ @entity.to_i }
173
229
  define_singleton_method(:types) { types }
174
230
  define_singleton_method(:members) { members }
231
+
232
+ # Return the offset of a struct member given its name.
233
+ # For example:
234
+ #
235
+ # MyStruct = struct [
236
+ # "int64_t i",
237
+ # "char c",
238
+ # ]
239
+ #
240
+ # MyStruct.offsetof("i") # => 0
241
+ # MyStruct.offsetof("c") # => 8
242
+ #
243
+ define_singleton_method(:offsetof) { |name|
244
+ klass.offsetof(name, members, types)
245
+ }
175
246
  members.each{|name|
176
247
  name = name[0] if name.is_a?(Array) # name is a nested struct
177
248
  next if method_defined?(name)
data/lib/fiddle/value.rb CHANGED
@@ -6,17 +6,17 @@ module Fiddle
6
6
  def unsigned_value(val, ty)
7
7
  case ty.abs
8
8
  when TYPE_CHAR
9
- [val].pack("c").unpack("C")[0]
9
+ [val].pack("c").unpack1("C")
10
10
  when TYPE_SHORT
11
- [val].pack("s!").unpack("S!")[0]
11
+ [val].pack("s!").unpack1("S!")
12
12
  when TYPE_INT
13
- [val].pack("i!").unpack("I!")[0]
13
+ [val].pack("i!").unpack1("I!")
14
14
  when TYPE_LONG
15
- [val].pack("l!").unpack("L!")[0]
15
+ [val].pack("l!").unpack1("L!")
16
16
  else
17
17
  if defined?(TYPE_LONG_LONG) and
18
18
  ty.abs == TYPE_LONG_LONG
19
- [val].pack("q").unpack("Q")[0]
19
+ [val].pack("q").unpack1("Q")
20
20
  else
21
21
  val
22
22
  end
@@ -26,17 +26,17 @@ module Fiddle
26
26
  def signed_value(val, ty)
27
27
  case ty.abs
28
28
  when TYPE_CHAR
29
- [val].pack("C").unpack("c")[0]
29
+ [val].pack("C").unpack1("c")
30
30
  when TYPE_SHORT
31
- [val].pack("S!").unpack("s!")[0]
31
+ [val].pack("S!").unpack1("s!")
32
32
  when TYPE_INT
33
- [val].pack("I!").unpack("i!")[0]
33
+ [val].pack("I!").unpack1("i!")
34
34
  when TYPE_LONG
35
- [val].pack("L!").unpack("l!")[0]
35
+ [val].pack("L!").unpack1("l!")
36
36
  else
37
37
  if defined?(TYPE_LONG_LONG) and
38
38
  ty.abs == TYPE_LONG_LONG
39
- [val].pack("Q").unpack("q")[0]
39
+ [val].pack("Q").unpack1("q")
40
40
  else
41
41
  val
42
42
  end
@@ -80,11 +80,11 @@ module Fiddle
80
80
  else
81
81
  case SIZEOF_VOIDP
82
82
  when SIZEOF_LONG
83
- return [arg].pack("p").unpack("l!")[0]
83
+ return [arg].pack("p").unpack1("l!")
84
84
  else
85
85
  if defined?(SIZEOF_LONG_LONG) and
86
86
  SIZEOF_VOIDP == SIZEOF_LONG_LONG
87
- return [arg].pack("p").unpack("q")[0]
87
+ return [arg].pack("p").unpack1("q")
88
88
  else
89
89
  raise(RuntimeError, "sizeof(void*)?")
90
90
  end
@@ -102,10 +102,8 @@ module Fiddle
102
102
  return val.unpack('C*')
103
103
  end
104
104
  end
105
- return arg
106
- else
107
- return arg
108
105
  end
106
+ return arg
109
107
  else
110
108
  if( arg.respond_to?(:to_ptr) )
111
109
  return arg.to_ptr.to_i
@@ -1,3 +1,3 @@
1
1
  module Fiddle
2
- VERSION = "1.0.9"
2
+ VERSION = "1.1.2"
3
3
  end
data/lib/fiddle.rb CHANGED
@@ -58,7 +58,36 @@ module Fiddle
58
58
  #
59
59
  # See Fiddle::Handle.new for more.
60
60
  def dlopen library
61
- Fiddle::Handle.new library
61
+ begin
62
+ Fiddle::Handle.new(library)
63
+ rescue DLError => error
64
+ case RUBY_PLATFORM
65
+ when /linux/
66
+ case error.message
67
+ when /\A(\/.+?): (?:invalid ELF header|file too short)/
68
+ # This may be a linker script:
69
+ # https://sourceware.org/binutils/docs/ld.html#Scripts
70
+ path = $1
71
+ else
72
+ raise
73
+ end
74
+ else
75
+ raise
76
+ end
77
+
78
+ File.open(path) do |input|
79
+ input.each_line do |line|
80
+ case line
81
+ when /\A\s*(?:INPUT|GROUP)\s*\(\s*([^\s,\)]+)/
82
+ # TODO: Should we support multiple files?
83
+ return dlopen($1)
84
+ end
85
+ end
86
+ end
87
+
88
+ # Not found
89
+ raise
90
+ end
62
91
  end
63
92
  module_function :dlopen
64
93
 
@@ -67,4 +96,8 @@ module Fiddle
67
96
  RTLD_GLOBAL = Handle::RTLD_GLOBAL # :nodoc:
68
97
  RTLD_LAZY = Handle::RTLD_LAZY # :nodoc:
69
98
  RTLD_NOW = Handle::RTLD_NOW # :nodoc:
99
+
100
+ Fiddle::Types.constants.each do |type|
101
+ const_set "TYPE_#{type}", Fiddle::Types.const_get(type)
102
+ end
70
103
  end
metadata CHANGED
@@ -1,58 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fiddle
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.9
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Patterson
8
8
  - SHIBATA Hiroshi
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-06-19 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: bundler
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - ">="
19
- - !ruby/object:Gem::Version
20
- version: '0'
21
- type: :development
22
- prerelease: false
23
- version_requirements: !ruby/object:Gem::Requirement
24
- requirements:
25
- - - ">="
26
- - !ruby/object:Gem::Version
27
- version: '0'
28
- - !ruby/object:Gem::Dependency
29
- name: rake
30
- requirement: !ruby/object:Gem::Requirement
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: '0'
35
- type: :development
36
- prerelease: false
37
- version_requirements: !ruby/object:Gem::Requirement
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- version: '0'
42
- - !ruby/object:Gem::Dependency
43
- name: rake-compiler
44
- requirement: !ruby/object:Gem::Requirement
45
- requirements:
46
- - - ">="
47
- - !ruby/object:Gem::Version
48
- version: '0'
49
- type: :development
50
- prerelease: false
51
- version_requirements: !ruby/object:Gem::Requirement
52
- requirements:
53
- - - ">="
54
- - !ruby/object:Gem::Version
55
- version: '0'
12
+ date: 2023-11-08 00:00:00.000000000 Z
13
+ dependencies: []
56
14
  description: A libffi wrapper for Ruby.
57
15
  email:
58
16
  - aaron@tenderlovemaking.com
@@ -65,15 +23,12 @@ files:
65
23
  - LICENSE.txt
66
24
  - README.md
67
25
  - Rakefile
68
- - bin/downloader.rb
69
- - bin/extlibs.rb
70
26
  - ext/fiddle/closure.c
71
27
  - ext/fiddle/closure.h
72
28
  - ext/fiddle/conversions.c
73
29
  - ext/fiddle/conversions.h
74
30
  - ext/fiddle/depend
75
31
  - ext/fiddle/extconf.rb
76
- - ext/fiddle/extlibs
77
32
  - ext/fiddle/fiddle.c
78
33
  - ext/fiddle/fiddle.h
79
34
  - ext/fiddle/function.c
@@ -103,7 +58,7 @@ licenses:
103
58
  - BSD-2-Clause
104
59
  metadata:
105
60
  msys2_mingw_dependencies: libffi
106
- post_install_message:
61
+ post_install_message:
107
62
  rdoc_options: []
108
63
  require_paths:
109
64
  - lib
@@ -111,15 +66,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
111
66
  requirements:
112
67
  - - ">="
113
68
  - !ruby/object:Gem::Version
114
- version: 2.3.0
69
+ version: 2.5.0
115
70
  required_rubygems_version: !ruby/object:Gem::Requirement
116
71
  requirements:
117
72
  - - ">="
118
73
  - !ruby/object:Gem::Version
119
74
  version: '0'
120
75
  requirements: []
121
- rubygems_version: 3.3.0.dev
122
- signing_key:
76
+ rubygems_version: 3.5.0.dev
77
+ signing_key:
123
78
  specification_version: 4
124
79
  summary: A libffi wrapper for Ruby.
125
80
  test_files: []
data/bin/downloader.rb DELETED
@@ -1,331 +0,0 @@
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