ffi-libfuse 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e105d84b1d0f73af20845ed1d39e51ae61f9ca4998fa9caf505b4e87ae08eaa9
4
- data.tar.gz: 7f9013b530bc62e9fb4071899e13f191fdc720f662221da1cac0cd554a76bc5f
3
+ metadata.gz: 791ea233a078177b2dd6a9b1cf9a75a64ed05d243de71ebf52a8028b11795653
4
+ data.tar.gz: 45d9d235948d5c4cdd09cd8ae168914e82caad31df8a2ce1727d8ed6bb2ff9ad
5
5
  SHA512:
6
- metadata.gz: 72b0a464ab67be704cb14f3ce4b07f1f9a29d260ee50864dcf8c75cc24ba2eaf43aab6ae9f79fda9edb707cded39fde10e8504be7b8ca011b1a7d625969d62f6
7
- data.tar.gz: b85626a26f149dc1852ae5928cef67eb5dd7fc4353acb6bafa4fb0061aec38dce6d6e6c8137b216bc540263ba28f5b5f627f3af8feecb9bab417bbfc64df45ab
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
- # DSL methods for defining struct member accessors
121
+ # Class methods for defining struct member accessors
9
122
  module ClassMethods
10
- # Define both a reader and a writer for members
11
- # @param [Array<Symbol>] attrs the attribute names
12
- # @param [String] format
13
- # A format string containing a single %s to convert attr symbol to struct member
14
- # @return [void]
15
- def ffi_attr_accessor(*attrs, format: '%s')
16
- ffi_attr_reader(*attrs, format: format)
17
- ffi_attr_writer(*attrs, format: format)
18
- end
19
-
20
- #
21
- # Define a struct attribute reader for members
22
- # @param [Array<Symbol>]
23
- # attrs the attribute names used as the reader method name
24
- #
25
- # a trailing '?' will be stripped from attribute names for primary reader method name, and cause an
26
- # boolean alias method to be created.
27
- # @param [Proc|String] format
28
- # A Proc, or format string containing a single %s, to convert each attribute name to the corresponding
29
- # struct member name
30
- # @param [Boolean] simple
31
- # Controls how writer methods are defined using block
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
- alias_method "#{attr}?", attr if bool
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 a struct attribute writer
58
- # @param [Array<Symbol>] attrs the attribute names
59
- # trailing '?' will be stripped from attribute names
60
- # @param [String|Proc] format
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
- # An optional block to set the input value into the struct field.
66
- #
67
- # If simple is true then the struct field is set to the result of calling block with the input value,
68
- # otherwise the method is defined directly from the block. Use __method__[0..-1] to get the attribute name
69
- # and self.class.ffi_attr_writers[__method__[0..-1]] to get the struct member name
70
- # @return [void]
71
- def ffi_attr_writer(*attrs, format: '%s', simple: true, &block)
72
- attrs.each do |attr|
73
- attr = attr[..-2] if attr[-1] == '?'
74
-
75
- member = (format % attr).to_sym
76
- ffi_attr_writers[attr.to_sym] = member
77
- if !block
78
- define_method("#{attr}=") { |val| self[member] = val }
79
- elsif simple
80
- define_method("#{attr}=") { |val| self[member] = block.call(val) }
81
- else
82
- define_method("#{attr}=", &block)
83
- end
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
- # All defined readers
88
- # @return [Hash<Symbol,Symbol>] map of attr names to member names for which readers exist
89
- def ffi_attr_readers
90
- @ffi_attr_readers ||= {}
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
- # All defined writers
94
- # @return [Hash<Symbol,Symbol>] map of attr names to member names for which writers exist
95
- def ffi_attr_writers
96
- @ffi_attr_writers ||= {}
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
- def ffi_bitflag_accessor(attr, *flags)
101
- ffi_bitflag_reader(attr, *flags)
102
- ffi_bitflag_writer(attr, *flags)
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] attr the bitmask member
107
- # @param [Array<Symbol>] flags list of flags
108
- # @return [void]
109
- def ffi_bitflag_reader(attr, *flags)
110
- flags.each do |f|
111
- ffi_attr_reader(:"#{f}?", simple: false) { self[attr].include?(f) }
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] attr the bitmask member
117
- # @param [Array<Symbol>] flags list of flags
118
- # @return [void]
119
- def ffi_bitflag_writer(attr, *flags)
120
- flags.each do |f|
121
- ffi_attr_writer(f, simple: false) do |v|
122
- v ? self[attr] += [f] : self[attr] -= [f]
123
- v
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 the native struct from another object or list of properties
390
+ # Fill struct from another object or list of properties
134
391
  # @param [Object] from
135
- # for each attribute we call self.attr=(from.attr)
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
- self.class.ffi_attr_writers.each_key { |v| send("#{v}=", from.send(v)) if from.respond_to?(v) }
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.each_pair { |k, v| send("#{k}=", v) }
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 inspect
150
- "#{self.class.name} {#{self.class.ffi_attr_readers.keys.map { |r| "#{r}: #{send(r)} " }.join(',')}"
441
+ def ffi_attr(method)
442
+ %w[? =].include?(method[-1]) ? :"#{method[0..-2]}" : method
151
443
  end
152
444
 
153
- # Convert struct to hash
154
- # @return [Hash<Symbol,Object>] map of reader attribute name to value
155
- def to_h
156
- self.class.ffi_attr_readers.keys.each_with_object({}) { |r, h| h[r] = send(r) }
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
- case Platform::NAME
31
- when 'x86_64-darwin'
32
- # From https://github.com/golang/go/issues/8106 these functions are not defined on Darwin.
33
- class << self
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
- else
50
- raise
76
+ # rubocop:enable Naming/MethodParameterName
51
77
  end
52
78
  end
53
79
  end