ffi-libfuse 0.4.0 → 0.4.1
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 +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/ffi/accessors.rb +418 -106
- data/lib/ffi/devt.rb +33 -7
- data/lib/ffi/flock.rb +31 -27
- data/lib/ffi/libfuse/adapter/context.rb +1 -1
- data/lib/ffi/libfuse/adapter/debug.rb +1 -1
- data/lib/ffi/libfuse/adapter/fuse2_compat.rb +7 -7
- data/lib/ffi/libfuse/adapter/fuse3_support.rb +7 -7
- data/lib/ffi/libfuse/adapter/interrupt.rb +1 -1
- data/lib/ffi/libfuse/adapter/pathname.rb +1 -1
- data/lib/ffi/libfuse/adapter/ruby.rb +1 -1
- data/lib/ffi/libfuse/adapter/safe.rb +1 -1
- data/lib/ffi/libfuse/filesystem/virtual_dir.rb +2 -2
- data/lib/ffi/libfuse/filesystem/virtual_file.rb +1 -1
- data/lib/ffi/libfuse/filesystem/virtual_link.rb +1 -1
- data/lib/ffi/libfuse/fuse3.rb +5 -5
- data/lib/ffi/libfuse/fuse_args.rb +9 -19
- data/lib/ffi/libfuse/fuse_cmdline_opts.rb +19 -16
- data/lib/ffi/libfuse/fuse_config.rb +25 -22
- data/lib/ffi/libfuse/fuse_conn_info.rb +1 -1
- data/lib/ffi/libfuse/fuse_context.rb +2 -1
- data/lib/ffi/libfuse/fuse_loop_config.rb +68 -20
- data/lib/ffi/libfuse/main.rb +7 -3
- data/lib/ffi/libfuse/test_helper.rb +4 -6
- data/lib/ffi/libfuse/version.rb +1 -1
- data/lib/ffi/stat.rb +16 -9
- data/lib/ffi/stat_vfs.rb +1 -2
- data/lib/ffi/struct_wrapper.rb +6 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 791ea233a078177b2dd6a9b1cf9a75a64ed05d243de71ebf52a8028b11795653
|
4
|
+
data.tar.gz: 45d9d235948d5c4cdd09cd8ae168914e82caad31df8a2ce1727d8ed6bb2ff9ad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '00301169c26144390a94545130326eb20c4661af57c2fd612f2ba477ba957b1f76e316fafb7f6da41431a97bab3a6e05523dd9d40a6f1f276d956e2f04aecf5a'
|
7
|
+
data.tar.gz: a3ea2914410e7f2c4e284df9a0ed500a0c70adc59504bfd93dd398b1396be1467f07071b20d2b7bfd68be465e6c1151889975d747b33969d9a53bff9e9561f47
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [0.4.1](https://github.com/lwoggardner/ffi-libfuse/compare/v0.4.0...v0.4.1) (2024-10-26)
|
4
|
+
|
5
|
+
|
6
|
+
### Bug Fixes
|
7
|
+
|
8
|
+
* support alpine linux with musl libc and fuse 3.16 ([65d362d](https://github.com/lwoggardner/ffi-libfuse/commit/65d362d7f3e87bca426742cccaabc9f421e6fc38)), closes [#26](https://github.com/lwoggardner/ffi-libfuse/issues/26) [#27](https://github.com/lwoggardner/ffi-libfuse/issues/27)
|
9
|
+
|
3
10
|
## [0.4.0](https://github.com/lwoggardner/ffi-libfuse/compare/v0.3.4...v0.4.0) (2024-01-21)
|
4
11
|
|
5
12
|
|
data/lib/ffi/accessors.rb
CHANGED
@@ -3,157 +3,469 @@
|
|
3
3
|
require 'ffi'
|
4
4
|
|
5
5
|
module FFI
|
6
|
-
# Syntax sugar for FFI::Struct
|
6
|
+
# Syntax sugar for **FFI::Struct**
|
7
|
+
#
|
8
|
+
# Modules that include {Accessors} are automatically extended by {ClassMethods} which provides for defining reader and
|
9
|
+
# writer methods over struct field members.
|
10
|
+
#
|
11
|
+
# Although designed around needs of **FFI::Struct**, eg the ability to map natural ruby names to struct field names,
|
12
|
+
# this module can be used over anything that stores attributes in a Hash like structure.
|
13
|
+
# It provides equivalent method definitions to *Module#attr_(reader|writer|accessor)* except using the index methods
|
14
|
+
# *:[<member>]*, and *:[<member>]=* instead of managing instance variables.
|
15
|
+
#
|
16
|
+
# Additionally it supports boolean attributes with '?' aliases for reader methods, and keeps track of attribute
|
17
|
+
# definitions to support {#fill},{#to_h} etc.
|
18
|
+
#
|
19
|
+
# Standard instance variable based attributes defined through *#attr_(reader|writer|accessor)*
|
20
|
+
# also get these features.
|
21
|
+
# @example
|
22
|
+
# class MyStruct < FFI::Struct
|
23
|
+
# include FFI::Accessors
|
24
|
+
#
|
25
|
+
# layout(
|
26
|
+
# a: :int,
|
27
|
+
# b: :int,
|
28
|
+
# s_one: :string,
|
29
|
+
# enabled: :bool,
|
30
|
+
# t: TimeSpec,
|
31
|
+
# p: :pointer
|
32
|
+
# )
|
33
|
+
#
|
34
|
+
# ## Attribute reader, writer, accessor over struct fields
|
35
|
+
#
|
36
|
+
# # @!attribute [r] a
|
37
|
+
# # @return [Integer]
|
38
|
+
# ffi_attr_reader :a
|
39
|
+
#
|
40
|
+
# # @!attribute [w] b
|
41
|
+
# # @return [Integer]
|
42
|
+
# ffi_attr_writer :b
|
43
|
+
#
|
44
|
+
# # @!attribute [rw] one
|
45
|
+
# # @return [String]
|
46
|
+
# ffi_attr_accessor({ one: :s_one }) # => [:one, :one=] reads/writes field :s_one
|
47
|
+
#
|
48
|
+
# ## Boolean attributes!
|
49
|
+
#
|
50
|
+
# # @!attribute [rw] enabled?
|
51
|
+
# # @return [Boolean]
|
52
|
+
# ffi_attr_accessor(:enabled?) # => [:enabled, :enabled?, :enabled=]
|
53
|
+
#
|
54
|
+
# ## Simple block converters
|
55
|
+
#
|
56
|
+
# # @!attribute [rw] time
|
57
|
+
# # @return [Time]
|
58
|
+
# ffi_attr_reader(time: :t) do |timespec|
|
59
|
+
# Time.at(timespec.tv_sec, timespec.tv_nsec) # convert TimeSpec struct to ruby Time
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# ## Complex attribute methods
|
63
|
+
#
|
64
|
+
# # writer for :time needs additional attributes
|
65
|
+
# ffi_attr_writer_method(time: :t) do |sec, nsec=0|
|
66
|
+
# sec, nsec = [sec.sec, sec.nsec] if sec.is_a?(Time)
|
67
|
+
# self[:t][tv_sec] = sec
|
68
|
+
# self[:t][tv_nsec] = nsec
|
69
|
+
# time
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# # safe readers handling a NULL struct
|
73
|
+
# safe_attrs = %i[a b].to_h { |m| [:"#{m}_safe", m] } # =>{ a_safe: :a, b_safe: b }
|
74
|
+
# ffi_attr_reader_method(**safe_attrs) do |default: nil|
|
75
|
+
# next default if null?
|
76
|
+
#
|
77
|
+
# _attr, member = ffi_reader(__method__)
|
78
|
+
# self[member]
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# ## Standard accessors over for instance variables, still supports boolean, to_h, fill
|
82
|
+
#
|
83
|
+
# # @!attribute [rw] debug?
|
84
|
+
# # @return [Boolean]
|
85
|
+
# attr_accessor :debug?
|
86
|
+
#
|
87
|
+
# ## Private accessors
|
88
|
+
#
|
89
|
+
# private
|
90
|
+
#
|
91
|
+
# ffi_attr_accessor(pointer: :p)
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# # Fill from another MyStruct (or anything that quacks like a MyStruct with readers matching our writers)
|
95
|
+
# s = MyStruct.new.fill(other)
|
96
|
+
#
|
97
|
+
# # Fill from hash...
|
98
|
+
# s = MyStruct.new.fill(b:2, one: 'str', time: Time.now, enabled: true, debug: false) # => s
|
99
|
+
# s.values #=> (FFI::Struct method) [ 0, 2, 'str', true, <TimeSpec>, FFI::Pointer::NULL ]
|
100
|
+
#
|
101
|
+
# # Struct instance to hash
|
102
|
+
# s.to_h # => { a: 0, one: 'str', time: <Time>, enabled: true, debug: false }
|
103
|
+
#
|
104
|
+
# # Attribute methods
|
105
|
+
# s.a # => 0
|
106
|
+
# s.b = 3 # => 3
|
107
|
+
# s.enabled # => true
|
108
|
+
# s.enabled? # => true
|
109
|
+
# s.time= 0,50 # => Time<50 nanoseconds after epoch>
|
110
|
+
# s.time= Time.now # => Time<now>
|
111
|
+
# s.debug? # => false
|
112
|
+
# s.pointer # => NoMethodError, private method 'pointer' called for MyStruct
|
113
|
+
# s.send(:pointer=, some_pointer) # => some_pointer
|
114
|
+
# s.send(:pointer) # => some_pointer
|
115
|
+
#
|
116
|
+
# null_s = MyStruct.new(FFI::Pointer::NULL)
|
117
|
+
# null_s.b_safe(default: 10) # => 10
|
118
|
+
#
|
119
|
+
# @see ClassMethods
|
7
120
|
module Accessors
|
8
|
-
#
|
121
|
+
# Class methods for defining struct member accessors
|
9
122
|
module ClassMethods
|
10
|
-
#
|
11
|
-
# @
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# @
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
# @param [Proc] block
|
33
|
-
# An optional block to convert the struct field value into something more useful
|
34
|
-
#
|
35
|
-
# If simple is true then block takes the struct field value, otherwise method is defined directly from the block
|
36
|
-
# and should use __method__ to get the attribute name. and self.class.ffi_attr_readers[__method__] to get the
|
37
|
-
# member name if these are not available from enclosed variables.
|
38
|
-
# @return [void]
|
39
|
-
def ffi_attr_reader(*attrs, format: '%s', simple: true, &block)
|
40
|
-
attrs.each do |attr|
|
41
|
-
bool, attr = attr[-1] == '?' ? [true, attr[..-2]] : [false, attr]
|
42
|
-
|
43
|
-
member = (format.respond_to?(:call) ? format.call(attr) : format % attr).to_sym
|
44
|
-
ffi_attr_readers[attr.to_sym] = member
|
45
|
-
if !block
|
46
|
-
define_method(attr) { self[member] }
|
47
|
-
elsif simple
|
48
|
-
define_method(attr) { block.call(self[member]) }
|
49
|
-
else
|
50
|
-
define_method(attr, &block)
|
51
|
-
end
|
123
|
+
# Keep track of default visibility since define_method doesn't do this itself
|
124
|
+
# @visibility private
|
125
|
+
%i[public private protected].each do |visibility|
|
126
|
+
define_method(visibility) do |*args|
|
127
|
+
@default_visibility = visibility if args.empty?
|
128
|
+
super(*args)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# @visibility private
|
133
|
+
def default_visibility
|
134
|
+
@default_visibility ||= :public
|
135
|
+
end
|
136
|
+
|
137
|
+
# Standard instance variable based reader with support for boolean and integration with *to_h*, *inspect* etc..
|
138
|
+
#
|
139
|
+
# The *member* registered for each attribute will be its instance variable symbol (ie with a leading '@')
|
140
|
+
# @return [Array<Symbol]
|
141
|
+
def attr_reader(*args)
|
142
|
+
super(*args.map { |a| a[-1] == '?' ? a[0..-2] : a })
|
143
|
+
ffi_attr_reader_method(**args.to_h { |a| [a, :"@#{a[-1] == '?' ? a[0..-2] : a}"] })
|
144
|
+
end
|
52
145
|
|
53
|
-
|
146
|
+
# Standard instance variable based writer with support for booleans and integration with *fill* etc..
|
147
|
+
#
|
148
|
+
# The *member* registered for each attribute will be its instance variable symbol (ie with a leading '@')
|
149
|
+
def attr_writer(*args)
|
150
|
+
super(*args.map { |a| a[-1] == '?' ? a[0..-2] : a })
|
151
|
+
ffi_attr_writer_method(**args.to_h { |a| [a, :"@#{a[-1] == '?' ? a[0..-2] : a}"] })
|
152
|
+
end
|
153
|
+
|
154
|
+
# Override instance variable based accessor to build our enhanced readers and writers
|
155
|
+
def attr_accessor(*args)
|
156
|
+
attr_reader(*args) + attr_writer(*args)
|
157
|
+
end
|
158
|
+
|
159
|
+
# @!group Accessor Definition
|
160
|
+
|
161
|
+
# Define both reader and writer
|
162
|
+
# @return [Array<Symbol] list of methods defined
|
163
|
+
def ffi_attr_accessor(*attrs, **attrs_map)
|
164
|
+
ffi_attr_reader(*attrs, **attrs_map) + ffi_attr_writer(*attrs, **attrs_map)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Define reader methods for the given attributes
|
168
|
+
#
|
169
|
+
# @param [Array<Symbol>] attrs
|
170
|
+
# List of struct field members to treat as attributes
|
171
|
+
#
|
172
|
+
# a trailing '?' in an attribute name indicates a boolean reader.
|
173
|
+
# eg. :debug? will define the reader method :debug and an alias method :debug? => :debug,
|
174
|
+
#
|
175
|
+
# String values are converted to Symbol
|
176
|
+
#
|
177
|
+
# @param [Hash<Symbol,Symbol>] attrs_map
|
178
|
+
# Map of attribute name to struct field name - where field names don't fit natural ruby methods etc...
|
179
|
+
#
|
180
|
+
# A Hash value in *attrs* is also treated as an *attrs_map*. String keys/values are transformed to Symbol.
|
181
|
+
#
|
182
|
+
# @param [Proc] block
|
183
|
+
# An optional block taking a single argument (the struct field value) to convert into something more useful.
|
184
|
+
#
|
185
|
+
# This block is evaluated within the method using :instance_exec
|
186
|
+
# @return [Array<Symbol>] list of methods defined
|
187
|
+
def ffi_attr_reader(*attrs, **attrs_map, &block)
|
188
|
+
ffi_attr_reader_method(*attrs, **attrs_map) do
|
189
|
+
_attr, member = ffi_attr_reader_member(__method__)
|
190
|
+
val = self[member]
|
191
|
+
block ? instance_exec(val, &block) : val
|
54
192
|
end
|
55
193
|
end
|
56
194
|
|
57
|
-
# Define
|
58
|
-
#
|
59
|
-
#
|
60
|
-
# @param [
|
61
|
-
# A format string containing a single %s to convert attr symbol to struct member
|
62
|
-
# @param [Boolean] simple
|
63
|
-
# Controls how writer methods are defined using block
|
195
|
+
# Define reader methods directly from a block
|
196
|
+
#
|
197
|
+
# @param [Array<Symbol>] attrs see {ffi_attr_reader}
|
198
|
+
# @param [Hash<Symbol,Symbol>] attrs_map
|
64
199
|
# @param [Proc] block
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
200
|
+
# must allow zero arity, but can have additional optional arguments or keyword arguments.
|
201
|
+
#
|
202
|
+
# the block is evaluated using :instance_exec
|
203
|
+
#
|
204
|
+
# within block the attribute name is always the method name (`__method__`) and the associated struct field
|
205
|
+
# member name is from any attribute maps supplied; ie *attrs_map* or Hash value in *attrs*.
|
206
|
+
# They can be retrieved using {ffi_attr_reader_member}
|
207
|
+
#
|
208
|
+
# `attr, member = ffi_attr_reader_member(__method__)`
|
209
|
+
#
|
210
|
+
# if not supplied a reader will still be registered for each attribute and a boolean alias created if required
|
211
|
+
# @return [Array<Symbol>] list of methods defined
|
212
|
+
# @example Related struct members
|
213
|
+
# # uid/gid are only meaningful if corresponding set_ field is true
|
214
|
+
# layout(set_uid: :bool, uid: :uint, set_gid: :bool, gid: :uint)
|
215
|
+
#
|
216
|
+
# # @!attribute [r] uid
|
217
|
+
# # @return [Integer] the user id
|
218
|
+
# # @return [nil] if uid has not been explicitly set
|
219
|
+
#
|
220
|
+
# # @!attribute [r] gid
|
221
|
+
# # @return [Integer] the group id
|
222
|
+
# # @return [nil] if gid has not been explicitly set
|
223
|
+
#
|
224
|
+
# ffi_attr_reader_method(:uid, :gid) do
|
225
|
+
# attr, member = ffi_attr_reader_member(__method__)
|
226
|
+
# setter = :"set_#{attr}"
|
227
|
+
# self[setter] ? self[:attr] : nil
|
228
|
+
# end # => [:uid :gid]
|
229
|
+
def ffi_attr_reader_method(*attrs, **attrs_map, &block)
|
230
|
+
attr_methods = map_attributes(attrs, attrs_map).flat_map do |attr, member, bool|
|
231
|
+
ffi_attr_readers_map[attr] = member
|
232
|
+
define_method(attr, &block) if block
|
233
|
+
next attr unless bool
|
234
|
+
|
235
|
+
bool_alias = :"#{attr}?"
|
236
|
+
alias_method(bool_alias, attr)
|
237
|
+
[attr, bool_alias]
|
84
238
|
end
|
239
|
+
send(default_visibility, *attr_methods)
|
240
|
+
attr_methods
|
85
241
|
end
|
86
242
|
|
87
|
-
#
|
88
|
-
# @
|
89
|
-
|
90
|
-
|
243
|
+
# Define struct attribute writers for the given attributes
|
244
|
+
# @param [Array<Symbol>] attrs see {ffi_attr_reader}
|
245
|
+
# @param [Hash<Symbol,Symbol>] attrs_map
|
246
|
+
# @param [Proc<Object>] block
|
247
|
+
# An optional block taking a single argument to convert input value into a value to be placed in the underlying
|
248
|
+
# struct field
|
249
|
+
#
|
250
|
+
# This block is evaluated within the method using :instance_exec
|
251
|
+
# @return [Array<Symbol>] list of methods defined
|
252
|
+
def ffi_attr_writer(*attrs, **attrs_map, &block)
|
253
|
+
ffi_attr_writer_method(*attrs, **attrs_map) do |val|
|
254
|
+
_attr, member = ffi_attr_writer_member(__method__)
|
255
|
+
self[member] = block ? instance_exec(val, &block) : val
|
256
|
+
end
|
91
257
|
end
|
92
258
|
|
93
|
-
#
|
94
|
-
# @
|
95
|
-
|
96
|
-
|
259
|
+
# Define writer methods directly from a block
|
260
|
+
# @param [Array<Symbol>] attrs see {ffi_attr_reader}
|
261
|
+
# @param [Hash<Symbol,Symbol>] attrs_map
|
262
|
+
# @param [Proc] block
|
263
|
+
# must allow arity = 1, but can have additional optional arguments or keyword arguments.
|
264
|
+
#
|
265
|
+
# the block is evaluated using :instance_exec
|
266
|
+
#
|
267
|
+
# within block the attribute name is always the method name stripped of its trailing '='
|
268
|
+
# (`:"#{__method__[0..-2]}"`) and the associated struct field member name is from any attribute maps
|
269
|
+
# supplied. ie *attrs_map* or Hash value in *attrs*. They can be retrieved using {ffi_attr_writer_member}
|
270
|
+
#
|
271
|
+
# `attr, member = ffi_attr_writer_member(__method__)`
|
272
|
+
#
|
273
|
+
# if not supplied a writer method is still registered for each attribute name
|
274
|
+
# @return [Array<Symbol>] list of methods defined
|
275
|
+
def ffi_attr_writer_method(*attrs, **attrs_map, &block)
|
276
|
+
writer_methods = map_attributes(attrs, attrs_map) do |attr, member, _bool|
|
277
|
+
ffi_attr_writers_map[attr] = member
|
278
|
+
block ? define_method("#{attr}=", &block) : attr
|
279
|
+
end
|
280
|
+
send(default_visibility, *writer_methods)
|
281
|
+
writer_methods
|
97
282
|
end
|
98
283
|
|
99
284
|
# Define individual flag accessors over a bitmask field
|
100
|
-
|
101
|
-
|
102
|
-
|
285
|
+
# @return [Array<Symbol>] list of methods defined
|
286
|
+
def ffi_bitflag_accessor(member, *flags)
|
287
|
+
ffi_bitflag_reader(member, *flags)
|
288
|
+
ffi_bitflag_writer(member, *flags)
|
103
289
|
end
|
104
290
|
|
105
291
|
# Define individual flag readers over a bitmask field
|
106
|
-
# @param [Symbol]
|
107
|
-
# @param [Array<Symbol>] flags list of flags
|
108
|
-
# @return [
|
109
|
-
def ffi_bitflag_reader(
|
110
|
-
flags.
|
111
|
-
|
292
|
+
# @param [Symbol] member the bitmask member
|
293
|
+
# @param [Array<Symbol>] flags list of flags to define methods for. Each flag also gets a :flag? boolean alias
|
294
|
+
# @return [Array<Symbol>] list of methods defined
|
295
|
+
def ffi_bitflag_reader(member, *flags)
|
296
|
+
bool_attrs = flags.to_h { |f| [:"#{f}?", member] }
|
297
|
+
ffi_attr_reader_method(**bool_attrs) do
|
298
|
+
flag_attr, member = ffi_attr_reader_member(__method__)
|
299
|
+
self[member].include?(flag_attr)
|
112
300
|
end
|
113
301
|
end
|
114
302
|
|
115
303
|
# Define individual flag writers over a bitmask field
|
116
|
-
# @param [Symbol]
|
117
|
-
# @param [Array<Symbol>] flags list of
|
118
|
-
# @return [
|
119
|
-
def ffi_bitflag_writer(
|
120
|
-
flags.
|
121
|
-
|
122
|
-
|
123
|
-
|
304
|
+
# @param [Symbol] member the bitmask member
|
305
|
+
# @param [Array<Symbol>] flags list of flag attributes
|
306
|
+
# @return [Array<Symbol>] list of methods defined
|
307
|
+
def ffi_bitflag_writer(member, *flags)
|
308
|
+
writers = flags.to_h { |f| [f, member] }
|
309
|
+
ffi_attr_writer_method(**writers) do |v|
|
310
|
+
flag_attr, member = ffi_attr_writer_member(__method__)
|
311
|
+
v ? self[member] += [flag_attr] : self[member] -= flag
|
312
|
+
v
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# @!endgroup
|
317
|
+
# @!group Accessor Information
|
318
|
+
|
319
|
+
# @return [Array<Symbol>]
|
320
|
+
# list of public attr accessor reader methods
|
321
|
+
def ffi_public_attr_readers
|
322
|
+
ffi_attr_readers & public_instance_methods
|
323
|
+
end
|
324
|
+
|
325
|
+
# @return [Array<Symbol>]
|
326
|
+
# list of accessor reader methods defined. (excludes boolean aliases)
|
327
|
+
def ffi_attr_readers
|
328
|
+
ffi_attr_readers_map.keys
|
329
|
+
end
|
330
|
+
|
331
|
+
# @return [Array<Symbol>]
|
332
|
+
# list of accessor writer methods (ie ending in '=')
|
333
|
+
def ffi_attr_writers
|
334
|
+
ffi_attr_writers_map.keys.map { |a| :"#{a}=" }
|
335
|
+
end
|
336
|
+
|
337
|
+
# @return [Array<Symbol>]
|
338
|
+
# list of public accessor writer methods (ie ending in '=')
|
339
|
+
def ffi_public_attr_writers
|
340
|
+
ffi_attr_writers & public_instance_methods
|
341
|
+
end
|
342
|
+
|
343
|
+
# @!endgroup
|
344
|
+
|
345
|
+
# @!visibility private
|
346
|
+
def ffi_attr_readers_map
|
347
|
+
@ffi_attr_readers_map ||= {}
|
348
|
+
end
|
349
|
+
|
350
|
+
# @!visibility private
|
351
|
+
def ffi_attr_writers_map
|
352
|
+
@ffi_attr_writers_map ||= {}
|
353
|
+
end
|
354
|
+
|
355
|
+
private
|
356
|
+
|
357
|
+
def map_attributes(attrs, attrs_map)
|
358
|
+
return enum_for(__method__, attrs, attrs_map) unless block_given?
|
359
|
+
|
360
|
+
attrs << attrs_map unless attrs_map.empty?
|
361
|
+
|
362
|
+
attrs.flat_map do |attr_entry|
|
363
|
+
case attr_entry
|
364
|
+
when Symbol, String
|
365
|
+
bool, attr = bool_attr(attr_entry)
|
366
|
+
|
367
|
+
yield attr, attr, bool
|
368
|
+
when Hash
|
369
|
+
attr_entry.flat_map do |attr, member|
|
370
|
+
bool, attr = bool_attr(attr)
|
371
|
+
yield attr, member.to_sym, bool
|
372
|
+
end
|
373
|
+
else
|
374
|
+
raise ArgumentError
|
124
375
|
end
|
125
376
|
end
|
126
377
|
end
|
378
|
+
|
379
|
+
def bool_attr(attr)
|
380
|
+
attr[-1] == '?' ? [true, attr[..-2].to_sym] : [false, attr.to_sym]
|
381
|
+
end
|
127
382
|
end
|
128
383
|
|
384
|
+
# @!parse extend ClassMethods
|
385
|
+
# @!visibility private
|
129
386
|
def self.included(mod)
|
130
387
|
mod.extend(ClassMethods)
|
131
388
|
end
|
132
389
|
|
133
|
-
# Fill
|
390
|
+
# Fill struct from another object or list of properties
|
134
391
|
# @param [Object] from
|
135
|
-
# for
|
392
|
+
# if from is a Hash then its is merged with args, otherwise look for corresponding readers on from, for our
|
393
|
+
# public writer attributes
|
136
394
|
# @param [Hash<Symbol,Object>] args
|
137
395
|
# for each entry <attr,val> we call self.attr=(val)
|
396
|
+
# @raise [ArgumentError] if args contains properties that do not have public writers
|
138
397
|
# @return [self]
|
139
398
|
def fill(from = nil, **args)
|
399
|
+
ffi_attr_fill(from, writers: self.class.ffi_public_attr_writers, **args)
|
400
|
+
end
|
401
|
+
|
402
|
+
# Inspect attributes
|
403
|
+
# @param [Array<Symbol>] readers list of attribute names to include in inspect, defaults to all readers
|
404
|
+
# @return [String]
|
405
|
+
def inspect(readers: self.class.ffi_public_attr_readers)
|
406
|
+
"#{self.class.name} {#{readers.map { |r| "#{r}: #{send(r)} " }.join(',')}"
|
407
|
+
end
|
408
|
+
|
409
|
+
# Convert struct to hash
|
410
|
+
# @param [Array<Symbol>] readers list of attribute names to include in hash, defaults to all public readers.
|
411
|
+
# @return [Hash<Symbol,Object>] map of attribute name to value
|
412
|
+
def to_h(readers: self.class.ffi_public_attr_readers)
|
413
|
+
readers.to_h { |r| [r, send(r)] }
|
414
|
+
end
|
415
|
+
|
416
|
+
private
|
417
|
+
|
418
|
+
# @!visibility public
|
419
|
+
# *(private)* Fill struct from another object or list of properties
|
420
|
+
# @param [Object] from
|
421
|
+
# @param [Hash<Symbol>] args
|
422
|
+
# @param [Array<Symbol>] writers list of allowed writer methods
|
423
|
+
# @raise [ArgumentError] if args contains properties not included in writers list
|
424
|
+
# @note This *private* method allows an including classes' instance method to
|
425
|
+
# fill attributes through any writer method (vs #{fill} which only sets attributes with public writers)
|
426
|
+
def ffi_attr_fill(from, writers: self.class.ffi_attr_writers, **args)
|
140
427
|
if from.is_a?(Hash)
|
141
428
|
args.merge!(from)
|
142
429
|
else
|
143
|
-
|
430
|
+
writers.each do |w|
|
431
|
+
r = w[0..-2] # strip trailing =
|
432
|
+
send(w, from.public_send(r)) if from.respond_to?(r)
|
433
|
+
end
|
144
434
|
end
|
145
|
-
args.
|
435
|
+
args.transform_keys! { |k| :"#{k}=" }
|
436
|
+
|
437
|
+
args.each_pair { |k, v| send(k, v) }
|
146
438
|
self
|
147
439
|
end
|
148
440
|
|
149
|
-
def
|
150
|
-
|
441
|
+
def ffi_attr(method)
|
442
|
+
%w[? =].include?(method[-1]) ? :"#{method[0..-2]}" : method
|
151
443
|
end
|
152
444
|
|
153
|
-
#
|
154
|
-
|
155
|
-
|
156
|
-
|
445
|
+
# @!group Private Accessor helpers
|
446
|
+
|
447
|
+
# @!visibility public
|
448
|
+
# *(private)* Takes `__method__` and returns the corresponding attr and struct member names
|
449
|
+
# @param [Symbol] attr_method typically `__method__` (or `__callee__`)
|
450
|
+
# @param [Symbol] default default if method is not a reader method
|
451
|
+
# @return [Array<Symbol,Symbol>] attr,member
|
452
|
+
# @raise [KeyError] if method has not been defined as a reader and no default is supplied
|
453
|
+
def ffi_attr_reader_member(attr_method, *default)
|
454
|
+
attr = ffi_attr(attr_method)
|
455
|
+
[attr, self.class.ffi_attr_readers_map.fetch(attr, *default)]
|
157
456
|
end
|
457
|
+
|
458
|
+
# @!visibility public
|
459
|
+
# *(private)* Takes `__method__` and returns the corresponding attr and struct member names
|
460
|
+
# @param [Symbol] attr_method typically `__method__` (or `__callee__`)
|
461
|
+
# @param [Symbol|nil] default default if method is not a writer method
|
462
|
+
# @return [Array<Symbol,Symbol>] attr,member
|
463
|
+
# @raise [KeyError] if method has not been defined as a writer and no default is supplied
|
464
|
+
def ffi_attr_writer_member(attr_method, *default)
|
465
|
+
attr = ffi_attr(attr_method)
|
466
|
+
[attr, self.class.ffi_attr_writers_map.fetch(attr, *default)]
|
467
|
+
end
|
468
|
+
|
469
|
+
# @!endgroup
|
158
470
|
end
|
159
471
|
end
|
data/lib/ffi/devt.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'ffi'
|
4
|
-
|
5
4
|
module FFI
|
6
5
|
# Calculate major/minor device numbers for use with mknod etc..
|
7
6
|
# @see makedev(3)
|
@@ -27,10 +26,13 @@ module FFI
|
|
27
26
|
# @return [Integer] the minor component of dev
|
28
27
|
attach_function :minor, :"#{prefix}minor", [:int], :int
|
29
28
|
rescue FFI::NotFoundError
|
30
|
-
|
31
|
-
|
32
|
-
#
|
33
|
-
|
29
|
+
|
30
|
+
class << self
|
31
|
+
# rubocop:disable Naming/MethodParameterName
|
32
|
+
case RUBY_PLATFORM
|
33
|
+
when 'x86_64-darwin'
|
34
|
+
# From https://github.com/golang/go/issues/8106 these functions are not defined on Darwin.
|
35
|
+
|
34
36
|
# define major(x) ((int32_t)(((u_int32_t)(x) >> 24) & 0xff))
|
35
37
|
def major(dev)
|
36
38
|
(dev >> 24) & 0xff
|
@@ -45,9 +47,33 @@ module FFI
|
|
45
47
|
def makedev(major, minor)
|
46
48
|
(major << 24) | minor
|
47
49
|
end
|
50
|
+
|
51
|
+
when 'x86_64-linux-musl' # eg alpine linux
|
52
|
+
# #define major(x) \
|
53
|
+
# ((unsigned)( (((x)>>31>>1) & 0xfffff000) | (((x)>>8) & 0x00000fff) ))
|
54
|
+
def major(x)
|
55
|
+
((x >> 31 >> 1) & 0xfffff000) | ((x >> 8) & 0x00000fff)
|
56
|
+
end
|
57
|
+
|
58
|
+
# #define minor(x) \
|
59
|
+
# ((unsigned)( (((x)>>12) & 0xffffff00) | ((x) & 0x000000ff) ))
|
60
|
+
#
|
61
|
+
def minor(x)
|
62
|
+
((x >> 12) & 0xffffff00) | (x & 0x000000ff)
|
63
|
+
end
|
64
|
+
|
65
|
+
# #define makedev(x,y) ( \
|
66
|
+
# (((x)&0xfffff000ULL) << 32) | \
|
67
|
+
# (((x)&0x00000fffULL) << 8) | \
|
68
|
+
# (((y)&0xffffff00ULL) << 12) | \
|
69
|
+
# (((y)&0x000000ffULL)) )
|
70
|
+
def makedev(x, y)
|
71
|
+
((x & 0xfffff000) << 32) | ((x & 0x00000fff) << 8) | ((y & 0xffffff00) << 12) | (y & 0x000000ff)
|
72
|
+
end
|
73
|
+
else
|
74
|
+
raise
|
48
75
|
end
|
49
|
-
|
50
|
-
raise
|
76
|
+
# rubocop:enable Naming/MethodParameterName
|
51
77
|
end
|
52
78
|
end
|
53
79
|
end
|