fiddle 1.0.9 → 1.1.2

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