ffi-libfuse 0.3.4 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|