vigilem-support 0.0.9

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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/lib/vigilem/ffi.rb +19 -0
  3. data/lib/vigilem/ffi/array_pointer_sync.rb +382 -0
  4. data/lib/vigilem/ffi/struct.rb +54 -0
  5. data/lib/vigilem/ffi/utils.rb +240 -0
  6. data/lib/vigilem/ffi/utils/struct.rb +93 -0
  7. data/lib/vigilem/support.rb +31 -0
  8. data/lib/vigilem/support/core_ext.rb +13 -0
  9. data/lib/vigilem/support/core_ext/debug_puts.rb +5 -0
  10. data/lib/vigilem/support/core_ext/enumerable.rb +25 -0
  11. data/lib/vigilem/support/key_map.rb +323 -0
  12. data/lib/vigilem/support/key_map_info.rb +85 -0
  13. data/lib/vigilem/support/lazy_simple_delegator.rb +81 -0
  14. data/lib/vigilem/support/max_size_error.rb +17 -0
  15. data/lib/vigilem/support/metadata.rb +13 -0
  16. data/lib/vigilem/support/obj_space.rb +28 -0
  17. data/lib/vigilem/support/patch/cucumber/c_lexer.rb +30 -0
  18. data/lib/vigilem/support/patch/ffi/pointer.rb +19 -0
  19. data/lib/vigilem/support/size_error.rb +6 -0
  20. data/lib/vigilem/support/sys.rb +5 -0
  21. data/lib/vigilem/support/system.rb +92 -0
  22. data/lib/vigilem/support/transmutable_hash.rb +206 -0
  23. data/lib/vigilem/support/utils.rb +224 -0
  24. data/lib/vigilem/support/version.rb +5 -0
  25. data/spec/spec_helper.rb +9 -0
  26. data/spec/vigilem/ffi/array_pointer_sync_spec.rb +728 -0
  27. data/spec/vigilem/ffi/struct_spec.rb +22 -0
  28. data/spec/vigilem/ffi/utils/struct_spec.rb +79 -0
  29. data/spec/vigilem/ffi/utils_spec.rb +240 -0
  30. data/spec/vigilem/support/core_ext/enumerable_spec.rb +38 -0
  31. data/spec/vigilem/support/data/cached.kmap +130 -0
  32. data/spec/vigilem/support/data/cached_UTF-8_del.kmap +142 -0
  33. data/spec/vigilem/support/data/cached_short.kmap +38 -0
  34. data/spec/vigilem/support/data/dump_keys_short.kmap +128 -0
  35. data/spec/vigilem/support/key_map_spec.rb +767 -0
  36. data/spec/vigilem/support/lazy_simple_delegator_spec.rb +77 -0
  37. data/spec/vigilem/support/obj_space_spec.rb +47 -0
  38. data/spec/vigilem/support/sys_spec.rb +21 -0
  39. data/spec/vigilem/support/system_spec.rb +31 -0
  40. data/spec/vigilem/support/transmutable_hash_spec.rb +175 -0
  41. data/spec/vigilem/support/utils_spec.rb +214 -0
  42. metadata +240 -0
@@ -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
@@ -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