ffi-libfuse 0.3.4 → 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 +29 -0
- data/README.md +1 -1
- data/lib/ffi/accessors.rb +419 -93
- data/lib/ffi/boolean_int.rb +1 -1
- data/lib/ffi/devt.rb +36 -10
- data/lib/ffi/flock.rb +31 -27
- data/lib/ffi/libfuse/adapter/context.rb +1 -1
- data/lib/ffi/libfuse/adapter/debug.rb +54 -16
- data/lib/ffi/libfuse/adapter/fuse2_compat.rb +43 -26
- data/lib/ffi/libfuse/adapter/fuse3_support.rb +7 -8
- 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 +211 -160
- data/lib/ffi/libfuse/adapter/safe.rb +70 -22
- data/lib/ffi/libfuse/callbacks.rb +2 -1
- data/lib/ffi/libfuse/filesystem/accounting.rb +1 -1
- data/lib/ffi/libfuse/filesystem/mapped_files.rb +33 -7
- data/lib/ffi/libfuse/filesystem/pass_through_dir.rb +0 -1
- data/lib/ffi/libfuse/filesystem/virtual_dir.rb +294 -127
- data/lib/ffi/libfuse/filesystem/virtual_file.rb +85 -79
- data/lib/ffi/libfuse/filesystem/virtual_fs.rb +34 -15
- data/lib/ffi/libfuse/filesystem/virtual_link.rb +60 -0
- data/lib/ffi/libfuse/filesystem/virtual_node.rb +104 -87
- data/lib/ffi/libfuse/filesystem.rb +1 -1
- data/lib/ffi/libfuse/fuse2.rb +3 -2
- data/lib/ffi/libfuse/fuse3.rb +6 -6
- data/lib/ffi/libfuse/fuse_args.rb +14 -21
- data/lib/ffi/libfuse/fuse_buf.rb +112 -0
- data/lib/ffi/libfuse/fuse_buf_vec.rb +228 -0
- data/lib/ffi/libfuse/fuse_cmdline_opts.rb +19 -16
- data/lib/ffi/libfuse/fuse_common.rb +10 -4
- data/lib/ffi/libfuse/fuse_config.rb +35 -23
- 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/fuse_operations.rb +86 -41
- data/lib/ffi/libfuse/gem_helper.rb +2 -9
- data/lib/ffi/libfuse/io.rb +56 -0
- data/lib/ffi/libfuse/main.rb +33 -26
- data/lib/ffi/libfuse/test_helper.rb +67 -61
- data/lib/ffi/libfuse/version.rb +1 -1
- data/lib/ffi/libfuse.rb +1 -1
- data/lib/ffi/stat/native.rb +4 -4
- data/lib/ffi/stat.rb +35 -12
- data/lib/ffi/stat_vfs.rb +1 -2
- data/lib/ffi/struct_array.rb +2 -1
- data/lib/ffi/struct_wrapper.rb +6 -4
- data/sample/hello_fs.rb +1 -1
- metadata +6 -3
- data/lib/ffi/libfuse/fuse_buffer.rb +0 -257
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,34 @@
|
|
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
|
+
|
10
|
+
## [0.4.0](https://github.com/lwoggardner/ffi-libfuse/compare/v0.3.4...v0.4.0) (2024-01-21)
|
11
|
+
|
12
|
+
|
13
|
+
### ⚠ BREAKING CHANGES
|
14
|
+
|
15
|
+
* **filesystem:** Fuse callbacks :init and :destroy are no longer passed on to sub-filesystems.
|
16
|
+
* **adapters:** Adapter::Debug now includes Adapter::Safe.
|
17
|
+
* Option parsing errors via raise exception rather than return false/nil
|
18
|
+
|
19
|
+
### Features
|
20
|
+
|
21
|
+
* **adapters:** Adapter::Debug now includes Adapter::Safe. ([a595304](https://github.com/lwoggardner/ffi-libfuse/commit/a59530427d7eb85961a724969eaa6ec099c5e4f6))
|
22
|
+
* **filesystem:** Support :rename operation in virtual filesystems ([a595304](https://github.com/lwoggardner/ffi-libfuse/commit/a59530427d7eb85961a724969eaa6ec099c5e4f6))
|
23
|
+
* **filesystem:** Support symlinks and hardlinks in virtual filesystems (VirtualDir/MemoryFS) ([a595304](https://github.com/lwoggardner/ffi-libfuse/commit/a59530427d7eb85961a724969eaa6ec099c5e4f6))
|
24
|
+
* Option parsing errors via raise exception rather than return false/nil ([a595304](https://github.com/lwoggardner/ffi-libfuse/commit/a59530427d7eb85961a724969eaa6ec099c5e4f6))
|
25
|
+
|
26
|
+
|
27
|
+
### Bug Fixes
|
28
|
+
|
29
|
+
* **fuse2compat:** Enhanced Fuse2 compatibility in Fuse2Compat module ([a595304](https://github.com/lwoggardner/ffi-libfuse/commit/a59530427d7eb85961a724969eaa6ec099c5e4f6))
|
30
|
+
* symlinks and hard links ([a595304](https://github.com/lwoggardner/ffi-libfuse/commit/a59530427d7eb85961a724969eaa6ec099c5e4f6))
|
31
|
+
|
3
32
|
## [0.3.4](https://github.com/lwoggardner/ffi-libfuse/compare/v0.3.3...v0.3.4) (2023-01-08)
|
4
33
|
|
5
34
|
|
data/README.md
CHANGED
data/lib/ffi/accessors.rb
CHANGED
@@ -3,143 +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
|
-
#
|
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
|
145
|
+
|
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
|
+
#
|
27
182
|
# @param [Proc] block
|
28
|
-
# An optional block
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
ffi_attr_readers[attr] = member
|
38
|
-
if !block
|
39
|
-
define_method(attr) { self[member] }
|
40
|
-
elsif simple
|
41
|
-
define_method(attr) { block.call(self[member]) }
|
42
|
-
else
|
43
|
-
define_method(attr, &block)
|
44
|
-
end
|
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
|
45
192
|
end
|
46
193
|
end
|
47
194
|
|
48
|
-
# Define
|
49
|
-
#
|
50
|
-
# @param [
|
51
|
-
#
|
52
|
-
# @param [Boolean] simple
|
53
|
-
# 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
|
54
199
|
# @param [Proc] block
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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]
|
72
238
|
end
|
239
|
+
send(default_visibility, *attr_methods)
|
240
|
+
attr_methods
|
73
241
|
end
|
74
242
|
|
75
|
-
#
|
76
|
-
# @
|
77
|
-
|
78
|
-
|
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
|
79
257
|
end
|
80
258
|
|
81
|
-
#
|
82
|
-
# @
|
83
|
-
|
84
|
-
|
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
|
85
282
|
end
|
86
283
|
|
87
284
|
# Define individual flag accessors over a bitmask field
|
88
|
-
|
89
|
-
|
90
|
-
|
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)
|
91
289
|
end
|
92
290
|
|
93
291
|
# Define individual flag readers over a bitmask field
|
94
|
-
# @param [Symbol]
|
95
|
-
# @param [Array<Symbol>] flags list of flags
|
96
|
-
# @return [
|
97
|
-
def ffi_bitflag_reader(
|
98
|
-
flags.
|
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)
|
300
|
+
end
|
99
301
|
end
|
100
302
|
|
101
303
|
# Define individual flag writers over a bitmask field
|
102
|
-
# @param [Symbol]
|
103
|
-
# @param [Array<Symbol>] flags list of
|
104
|
-
# @return [
|
105
|
-
def ffi_bitflag_writer(
|
106
|
-
flags.
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
110
375
|
end
|
111
376
|
end
|
112
377
|
end
|
378
|
+
|
379
|
+
def bool_attr(attr)
|
380
|
+
attr[-1] == '?' ? [true, attr[..-2].to_sym] : [false, attr.to_sym]
|
381
|
+
end
|
113
382
|
end
|
114
383
|
|
384
|
+
# @!parse extend ClassMethods
|
385
|
+
# @!visibility private
|
115
386
|
def self.included(mod)
|
116
387
|
mod.extend(ClassMethods)
|
117
388
|
end
|
118
389
|
|
119
|
-
# Fill
|
390
|
+
# Fill struct from another object or list of properties
|
120
391
|
# @param [Object] from
|
121
|
-
# 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
|
122
394
|
# @param [Hash<Symbol,Object>] args
|
123
395
|
# for each entry <attr,val> we call self.attr=(val)
|
396
|
+
# @raise [ArgumentError] if args contains properties that do not have public writers
|
124
397
|
# @return [self]
|
125
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)
|
126
427
|
if from.is_a?(Hash)
|
127
428
|
args.merge!(from)
|
128
429
|
else
|
129
|
-
|
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
|
130
434
|
end
|
131
|
-
args.
|
435
|
+
args.transform_keys! { |k| :"#{k}=" }
|
436
|
+
|
437
|
+
args.each_pair { |k, v| send(k, v) }
|
132
438
|
self
|
133
439
|
end
|
134
440
|
|
135
|
-
def
|
136
|
-
|
441
|
+
def ffi_attr(method)
|
442
|
+
%w[? =].include?(method[-1]) ? :"#{method[0..-2]}" : method
|
137
443
|
end
|
138
444
|
|
139
|
-
#
|
140
|
-
|
141
|
-
|
142
|
-
|
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)]
|
143
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
|
144
470
|
end
|
145
471
|
end
|