vigilem-support 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/vigilem/ffi.rb +19 -0
- data/lib/vigilem/ffi/array_pointer_sync.rb +382 -0
- data/lib/vigilem/ffi/struct.rb +54 -0
- data/lib/vigilem/ffi/utils.rb +240 -0
- data/lib/vigilem/ffi/utils/struct.rb +93 -0
- data/lib/vigilem/support.rb +31 -0
- data/lib/vigilem/support/core_ext.rb +13 -0
- data/lib/vigilem/support/core_ext/debug_puts.rb +5 -0
- data/lib/vigilem/support/core_ext/enumerable.rb +25 -0
- data/lib/vigilem/support/key_map.rb +323 -0
- data/lib/vigilem/support/key_map_info.rb +85 -0
- data/lib/vigilem/support/lazy_simple_delegator.rb +81 -0
- data/lib/vigilem/support/max_size_error.rb +17 -0
- data/lib/vigilem/support/metadata.rb +13 -0
- data/lib/vigilem/support/obj_space.rb +28 -0
- data/lib/vigilem/support/patch/cucumber/c_lexer.rb +30 -0
- data/lib/vigilem/support/patch/ffi/pointer.rb +19 -0
- data/lib/vigilem/support/size_error.rb +6 -0
- data/lib/vigilem/support/sys.rb +5 -0
- data/lib/vigilem/support/system.rb +92 -0
- data/lib/vigilem/support/transmutable_hash.rb +206 -0
- data/lib/vigilem/support/utils.rb +224 -0
- data/lib/vigilem/support/version.rb +5 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/vigilem/ffi/array_pointer_sync_spec.rb +728 -0
- data/spec/vigilem/ffi/struct_spec.rb +22 -0
- data/spec/vigilem/ffi/utils/struct_spec.rb +79 -0
- data/spec/vigilem/ffi/utils_spec.rb +240 -0
- data/spec/vigilem/support/core_ext/enumerable_spec.rb +38 -0
- data/spec/vigilem/support/data/cached.kmap +130 -0
- data/spec/vigilem/support/data/cached_UTF-8_del.kmap +142 -0
- data/spec/vigilem/support/data/cached_short.kmap +38 -0
- data/spec/vigilem/support/data/dump_keys_short.kmap +128 -0
- data/spec/vigilem/support/key_map_spec.rb +767 -0
- data/spec/vigilem/support/lazy_simple_delegator_spec.rb +77 -0
- data/spec/vigilem/support/obj_space_spec.rb +47 -0
- data/spec/vigilem/support/sys_spec.rb +21 -0
- data/spec/vigilem/support/system_spec.rb +31 -0
- data/spec/vigilem/support/transmutable_hash_spec.rb +175 -0
- data/spec/vigilem/support/utils_spec.rb +214 -0
- metadata +240 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ca7c5b14a32d34b7a9160a7eb60b96bc0654b3a5
|
4
|
+
data.tar.gz: 612804d8a3aef4f01e8587d595ecf1044147df9c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6d99905696f122267df2d1ff04eaa1b62e65d7c7c678d353272ccc639a93592ac840ca4bce74daecef07510f75d9b79bf533a734b3477cfb07ce18e8087d327a
|
7
|
+
data.tar.gz: 9cd01bad9a80179757b155212f3cf60e2c3407813b060b465c76f3962846bbbfdb0aef33bf5242fec47afc03f82b40f5437e627077824d91e757b150e9f6ad93
|
data/lib/vigilem/ffi.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module Vigilem
|
4
|
+
module FFI
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'vigilem/support'
|
9
|
+
|
10
|
+
require 'vigilem/ffi/utils'
|
11
|
+
FFIUtils = ::Vigilem::FFI::Utils
|
12
|
+
|
13
|
+
require 'vigilem/ffi/utils/struct'
|
14
|
+
FFIStructUtils = FFIUtils::Struct
|
15
|
+
|
16
|
+
require 'vigilem/ffi/struct'
|
17
|
+
VFFIStruct = ::Vigilem::FFI::Struct
|
18
|
+
|
19
|
+
require 'vigilem/ffi/array_pointer_sync'
|
@@ -0,0 +1,382 @@
|
|
1
|
+
module Vigilem::FFI
|
2
|
+
# creates an ary of fixed size that is synched with
|
3
|
+
# an underlying FFI::Pointer, kind of reinventing the wheel here
|
4
|
+
# if you think about it
|
5
|
+
# this can be split further for a more dynamic ary that can increase in
|
6
|
+
# size, but thats for the future
|
7
|
+
# @note ary is used to match ptr used by struct
|
8
|
+
module ArrayPointerSync
|
9
|
+
|
10
|
+
require 'vigilem/support/max_size_error'
|
11
|
+
|
12
|
+
attr_accessor :cache_hash, :max_size
|
13
|
+
|
14
|
+
#
|
15
|
+
# @raise [TypeError] '@max_size is nil'
|
16
|
+
# @return
|
17
|
+
def max_size!
|
18
|
+
@max_size || raise(TypeError, '@max_size is nil')
|
19
|
+
end
|
20
|
+
|
21
|
+
alias_method :max_len, :max_size
|
22
|
+
alias_method :max_len=, :max_size=
|
23
|
+
|
24
|
+
private :cache_hash=, :max_size=, :max_len=
|
25
|
+
|
26
|
+
#
|
27
|
+
# @return [FFI::Pointer]
|
28
|
+
def ptr
|
29
|
+
@ptr ||= FFI::MemoryPointer.new(self.class.ary_type, self.max_size!)
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# @param base
|
34
|
+
# @return
|
35
|
+
def self.included(base)
|
36
|
+
base.extend ClassMethods
|
37
|
+
base.class_eval do
|
38
|
+
native_type FFI::Type::POINTER
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
@point_cuts = (Array.instance_methods -
|
43
|
+
[*Array.instance_methods.grep(/method|taint|trust|instance_variable|send|class|respond/),
|
44
|
+
:__id__, :object_id, :extend, :freeze, :kind_of?, :instance_of?, :is_a?, :replace, :eql?, :equal?
|
45
|
+
]).sort
|
46
|
+
|
47
|
+
#
|
48
|
+
# @param [Symbol] method_name
|
49
|
+
# @param [Array] args
|
50
|
+
# @param [Proc] block
|
51
|
+
# @raise [RuntimeException]
|
52
|
+
# @see #out_of_bounds_check
|
53
|
+
# @return
|
54
|
+
def after_ary_method(method_name, return_value, *args, &block)
|
55
|
+
out_of_bounds_check
|
56
|
+
update
|
57
|
+
return_value
|
58
|
+
end
|
59
|
+
|
60
|
+
@point_cuts.each do |point_cut|
|
61
|
+
define_method(point_cut) do |*args, &block|
|
62
|
+
begin
|
63
|
+
update
|
64
|
+
ret = ary().__send__(point_cut, *args, &block)
|
65
|
+
method_name = __method__
|
66
|
+
if not [:size, :length].include? method_name
|
67
|
+
after_ary_method(method_name, ret, *args, &block)
|
68
|
+
else
|
69
|
+
ret
|
70
|
+
end
|
71
|
+
rescue StandardError => e
|
72
|
+
e.set_backtrace([__method__.to_s] + e.backtrace)
|
73
|
+
raise e
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# @todo change max_len_or_ptr_or_first_item, and default max_size to init_values.length
|
79
|
+
# @param [Integer || FFI::Pointer] max_len_or_ptr
|
80
|
+
# @param [Array] init_values
|
81
|
+
# @return [ArrayPointerSync] self
|
82
|
+
def initialize_ary_ptr_sync(max_len_or_ptr, *init_values)
|
83
|
+
if max_len_or_ptr.is_a? FFI::Pointer
|
84
|
+
if not (max_len_or_ptr and init_values.empty?)
|
85
|
+
raise ArgumentError, "Cannot have both a pointer and *init_values:`#{max_len_or_ptr.inspect}' `#{init_values.inspect}'"
|
86
|
+
end
|
87
|
+
@ptr = max_len_or_ptr
|
88
|
+
update_ary
|
89
|
+
@max_size = ary().size
|
90
|
+
else
|
91
|
+
raise TypeError, "max_len_or_ptr, doesn't respond_to? :to_i" unless max_len_or_ptr.respond_to? :to_i
|
92
|
+
@max_size = max_len_or_ptr.to_i
|
93
|
+
@ary = init_values
|
94
|
+
@ptr = ::FFI::MemoryPointer.new(self.class.ary_type, @max_size)
|
95
|
+
update_ptr if not init_values.empty?
|
96
|
+
end
|
97
|
+
update_cache
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
#
|
103
|
+
module ClassMethods
|
104
|
+
include FFI::DataConverter
|
105
|
+
|
106
|
+
# @todo create and ary_type_wrapper for symbols so an if stmnt
|
107
|
+
# isn;t always needed
|
108
|
+
# @raise [RuntimeError]
|
109
|
+
# @return [Class || Symbol]
|
110
|
+
def ary_type
|
111
|
+
raise NotImplementedError, "No ary_type configured for this class #{self}"
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
# @return [Class]
|
116
|
+
def ary_type_object
|
117
|
+
if ary_type.is_a? Symbol
|
118
|
+
::FFI.find_type(ary_type)
|
119
|
+
else
|
120
|
+
ary_type
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
#
|
125
|
+
# @return [Integer] the size
|
126
|
+
def ary_type_size
|
127
|
+
ary_type_object.size
|
128
|
+
end
|
129
|
+
|
130
|
+
#
|
131
|
+
# @param [Pointer] pointer
|
132
|
+
# @return [Integer]
|
133
|
+
def ptr_capacity(pointer)
|
134
|
+
pointer.size/ary_type_size
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# @return [Array<#ary_type>]
|
139
|
+
def ary_of_type(pointer)
|
140
|
+
Utils.ary_of_type(ary_type, ary_type_size, pointer)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# @see Array#replace
|
145
|
+
# @param [Array] other
|
146
|
+
# @return [ArrayPointerSync]
|
147
|
+
def replace(other)
|
148
|
+
ary.replace(other)
|
149
|
+
update
|
150
|
+
self
|
151
|
+
end
|
152
|
+
#
|
153
|
+
# @return [String]
|
154
|
+
def bytes
|
155
|
+
update
|
156
|
+
ptr.read_bytes(ptr.size)
|
157
|
+
end
|
158
|
+
|
159
|
+
# bytes of the idtem referenced by index
|
160
|
+
# @param [Integer] idx
|
161
|
+
# @return [String]
|
162
|
+
def bytes_of(idx)
|
163
|
+
update
|
164
|
+
ptr.get_bytes(idx * (type_size = self.class.ary_type_size), type_size)
|
165
|
+
end
|
166
|
+
|
167
|
+
#
|
168
|
+
# @return [Array<Integer>]
|
169
|
+
def offsets
|
170
|
+
if update or @ptr_offsets.nil?
|
171
|
+
_ptr_offsets
|
172
|
+
else
|
173
|
+
@ptr_offsets
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
#
|
178
|
+
# @raise [RuntimeError] when both ptr and ary changed
|
179
|
+
# @return [Hash] what item changed
|
180
|
+
def what_changed?
|
181
|
+
results = {ary: ary_changed?, ptr: ptr_changed? }
|
182
|
+
raise 'both ary and pointer changed' if results.values.all?
|
183
|
+
results
|
184
|
+
end
|
185
|
+
|
186
|
+
# checks to see if the the ptr contents
|
187
|
+
# or ary conents have changed
|
188
|
+
#
|
189
|
+
# @return [TrueClass || FalseClass]
|
190
|
+
def changed?
|
191
|
+
what_changed?.values.any?
|
192
|
+
end
|
193
|
+
|
194
|
+
#
|
195
|
+
# @return [TrueClass || FalseClass]
|
196
|
+
def ptr_changed?
|
197
|
+
ptr_cache_hash() != ptr_hash()
|
198
|
+
end
|
199
|
+
|
200
|
+
#
|
201
|
+
# @return [TrueClass || FalseClass]
|
202
|
+
def ary_changed?
|
203
|
+
self.cache_hash != ary().hash
|
204
|
+
end
|
205
|
+
|
206
|
+
# checks whether or not ary.size > max_size
|
207
|
+
# @return [TrueClass || FalseClass]
|
208
|
+
def out_of_bounds?
|
209
|
+
_size > max_size!
|
210
|
+
end
|
211
|
+
|
212
|
+
#
|
213
|
+
# @raises RuntimeException
|
214
|
+
# @return [NilClass]
|
215
|
+
def out_of_bounds_check
|
216
|
+
_raise_size_error if out_of_bounds?
|
217
|
+
end
|
218
|
+
|
219
|
+
#
|
220
|
+
# @param [Array] other
|
221
|
+
# @return [ArrayPointerSync]
|
222
|
+
def replace(other)
|
223
|
+
ary.replace(other)
|
224
|
+
update
|
225
|
+
self
|
226
|
+
end
|
227
|
+
|
228
|
+
#
|
229
|
+
#
|
230
|
+
# @return [Array]
|
231
|
+
def to_a
|
232
|
+
update
|
233
|
+
ary
|
234
|
+
end
|
235
|
+
|
236
|
+
# @todo
|
237
|
+
#def to_ary
|
238
|
+
#
|
239
|
+
#end
|
240
|
+
|
241
|
+
#
|
242
|
+
# @return [ArrayPointerSync]
|
243
|
+
def dup
|
244
|
+
self.class.new(ptr.dup)
|
245
|
+
end
|
246
|
+
|
247
|
+
# dup copies everything to a new pointer, is this needed? does it copy
|
248
|
+
# @return [ArrayPointerSync]
|
249
|
+
def deep_dup
|
250
|
+
pointer = FFI::MemoryPointer.new(self.class.ary_type, max_size)
|
251
|
+
pointer.write_bytes(self.bytes)
|
252
|
+
self.class.new(pointer)
|
253
|
+
end
|
254
|
+
|
255
|
+
# @return [String] just like that of an array
|
256
|
+
def to_s
|
257
|
+
update
|
258
|
+
ary.to_s
|
259
|
+
end
|
260
|
+
|
261
|
+
#
|
262
|
+
# @see Object#inspect
|
263
|
+
# @return [String]
|
264
|
+
def inspect
|
265
|
+
update
|
266
|
+
vars = instance_variables.except(:@ary).map {|var| "#{var}=#{instance_variable_get(var)}" }.join(' ')
|
267
|
+
"#{_to_s.chomp('>')} #{vars}>"
|
268
|
+
end
|
269
|
+
|
270
|
+
# the basic inspect structure without the vars
|
271
|
+
# @return [String]
|
272
|
+
def _to_s
|
273
|
+
"#<#{self.class}:0x#{object_id << 1} #{ary}>"
|
274
|
+
end
|
275
|
+
|
276
|
+
private
|
277
|
+
attr_accessor :ptr_cache_hash
|
278
|
+
|
279
|
+
#
|
280
|
+
# @return [Array]
|
281
|
+
def ary
|
282
|
+
@ary ||= []
|
283
|
+
end
|
284
|
+
|
285
|
+
#
|
286
|
+
# @return [Array<Integer>]
|
287
|
+
def _ptr_offsets
|
288
|
+
@ptr_offsets = 0.upto((ary.size - 1)).map {|n| self.class.ary_type_size * n }
|
289
|
+
end
|
290
|
+
|
291
|
+
# @see String#hash
|
292
|
+
# @return [Integer]
|
293
|
+
def ptr_hash
|
294
|
+
ptr.read_bytes(ptr.size).hash
|
295
|
+
end
|
296
|
+
|
297
|
+
# detects what changed and updates as needed
|
298
|
+
# @return [TrueClass || FalseClass] updated?
|
299
|
+
def update
|
300
|
+
if (results = what_changed?)[:ary]
|
301
|
+
update_ptr
|
302
|
+
update_ary_cache
|
303
|
+
true
|
304
|
+
elsif results[:ptr]
|
305
|
+
update_ary
|
306
|
+
update_ptr_cache
|
307
|
+
true
|
308
|
+
else
|
309
|
+
false
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
#
|
314
|
+
# this is slightly dangerous, if anything was still pointing to old pointer location
|
315
|
+
# now its being reclaimed, this will change it
|
316
|
+
# @return [Integer] hash
|
317
|
+
def update_ptr
|
318
|
+
ptr.clear
|
319
|
+
if (not (arry_type = self.class.ary_type).is_a?(Symbol))
|
320
|
+
if arry_type.respond_to? :to_native
|
321
|
+
ary.each {|item| ptr.write_pointer(arry_type.to_native(item, nil)) }
|
322
|
+
elsif arry_type.method_defined? :bytes
|
323
|
+
ptr.write_bytes(ary.map {|item| item.respond.bytes }.join)
|
324
|
+
elsif arry_type.method_defined? :pointer
|
325
|
+
ary.each do |item|
|
326
|
+
if item.size == item.pointer.size
|
327
|
+
ptr.write_bytes((itm_ptr = item.pointer).read_bytes(itm_ptr.size))
|
328
|
+
else
|
329
|
+
raise ArgumentError, "Cannot reliably convert `#{item}' to a native_type"
|
330
|
+
end
|
331
|
+
end
|
332
|
+
else
|
333
|
+
raise ArgumentError, "Cannot reliably convert `#{arry_type}' to a native_type"
|
334
|
+
end
|
335
|
+
else
|
336
|
+
Utils.put_array_typedef(ptr, arry_type, ary)
|
337
|
+
end
|
338
|
+
update_ptr_cache
|
339
|
+
#self.ptr_cache_hash = @bytes.hash # @FIXME ptr_hash() and @bytes.hash should be the same...
|
340
|
+
end
|
341
|
+
|
342
|
+
#
|
343
|
+
# @return [Array<self.class.ary_type>]
|
344
|
+
def update_ary
|
345
|
+
arry = ary.replace(self.class.ary_of_type(ptr))
|
346
|
+
_ptr_offsets
|
347
|
+
update_ary_cache
|
348
|
+
arry
|
349
|
+
end
|
350
|
+
|
351
|
+
# size method on ary without update
|
352
|
+
# @return [Integer] size
|
353
|
+
def _size
|
354
|
+
ary.size
|
355
|
+
end
|
356
|
+
|
357
|
+
#
|
358
|
+
# @return [Array<Integer>]
|
359
|
+
def update_cache
|
360
|
+
[update_ary_cache, update_ptr_cache]
|
361
|
+
end
|
362
|
+
|
363
|
+
#
|
364
|
+
# @return [Integer] hash
|
365
|
+
def update_ptr_cache
|
366
|
+
self.ptr_cache_hash = ptr_hash()
|
367
|
+
end
|
368
|
+
|
369
|
+
#
|
370
|
+
# @return [Integer] hash
|
371
|
+
def update_ary_cache
|
372
|
+
self.cache_hash = ary().hash
|
373
|
+
end
|
374
|
+
|
375
|
+
#
|
376
|
+
# @raise [RuntimeError]
|
377
|
+
# @return
|
378
|
+
def _raise_size_error
|
379
|
+
raise Vigilem::Support::MaxSizeError, [_to_s, self.max_size!]
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Vigilem
|
2
|
+
module FFI
|
3
|
+
# ::FFI::Struct with some sugar
|
4
|
+
class Struct < ::FFI::Struct
|
5
|
+
|
6
|
+
include Utils::Struct
|
7
|
+
|
8
|
+
# should make assign private, but since FFI::Struct.rb
|
9
|
+
# doesn;t know it's memory location
|
10
|
+
# this may have to be updated from the outside
|
11
|
+
attr_accessor :ptr_offset
|
12
|
+
|
13
|
+
#
|
14
|
+
# @param [FFI::Pointer || Integer] ptr_or_offset
|
15
|
+
# @param [Integer] offset
|
16
|
+
def initialize(ptr_or_offset=nil, offset=0)
|
17
|
+
if ptr_or_offset.is_a? Integer
|
18
|
+
super()
|
19
|
+
@ptr_offset = ptr_or_offset
|
20
|
+
else
|
21
|
+
super(*ptr_or_offset)
|
22
|
+
@ptr_offset = offset
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# allows initial values in a new object like Hash::[]
|
27
|
+
# @todo move to utils/struct::ClassMethods
|
28
|
+
# @param [Array<Hash||Array>] *vals
|
29
|
+
# @return [Struct]
|
30
|
+
def self.[](*vals)
|
31
|
+
frst = vals.first
|
32
|
+
vals = frst if frst.is_a? Hash and vals.size == 1
|
33
|
+
new.bulk_assign(vals)
|
34
|
+
end
|
35
|
+
|
36
|
+
# converts struct to a String bytes "\x00" or "\u0000"
|
37
|
+
# the struct needs to know where in the pointer it is...
|
38
|
+
# @param [FFI::Struct] struct
|
39
|
+
# @return [String]
|
40
|
+
def bytes
|
41
|
+
ptr = self.to_ptr
|
42
|
+
ptr.get_bytes(self.ptr_offset, self.size)
|
43
|
+
end
|
44
|
+
|
45
|
+
# shows the members and thier values in addition to the traditional inspect
|
46
|
+
# @see Object#inspect
|
47
|
+
# @return [String]
|
48
|
+
def inspect
|
49
|
+
"#{super.chop} #{members.map {|mem| "#{mem}=#{self[mem]}" }.join(' ')}>"
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|