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
@@ -0,0 +1,224 @@
|
|
1
|
+
require 'vigilem/support/core_ext'
|
2
|
+
|
3
|
+
module Vigilem
|
4
|
+
module Support
|
5
|
+
#
|
6
|
+
# @todo 2 tables one for keysyms and the other for keycodes
|
7
|
+
module ArrayAndStringUtils
|
8
|
+
# splits an Array or String into parts at a
|
9
|
+
# location (rather than content) keeping the
|
10
|
+
# i-1 item in the lower "array"
|
11
|
+
# @param [#dup, #slice!] obj
|
12
|
+
# @param [Numeric] idx
|
13
|
+
# @return [Array]
|
14
|
+
def split_at(obj, idx)
|
15
|
+
[(copy = _deep_dup(obj)).slice!(0...idx), copy]
|
16
|
+
end
|
17
|
+
|
18
|
+
# works like split where the item split
|
19
|
+
# is removed
|
20
|
+
# @see #split_at
|
21
|
+
# @param [#dup, #slice!]
|
22
|
+
# @param [Numeric] idx
|
23
|
+
# @return [Array]
|
24
|
+
def split_on(obj, idx)
|
25
|
+
[(copy = _deep_dup(obj)).slice!(0...idx), copy.slice!(1..-1)]
|
26
|
+
end
|
27
|
+
|
28
|
+
# \#slice!s the array the length, and returns what is left over
|
29
|
+
# from the len `if len > obj.length`
|
30
|
+
# @param [#slice!, (#length || #size)] obj
|
31
|
+
# @param [Integer] len
|
32
|
+
# @return [Array<Object, Integer>] [obj #slice!ed, remainder of (len - obj.size)]
|
33
|
+
def offset!(obj, len) #@todo V should be nil not ''
|
34
|
+
[_offset_from_length(obj, len), (obj.slice!(0..len-1) || '')].reverse
|
35
|
+
end
|
36
|
+
|
37
|
+
# @see #offset!
|
38
|
+
# @param [#dup, #slice!, #length] obj
|
39
|
+
# @param [Integer] len
|
40
|
+
# @return [Array<Object, Integer>] [obj #slice!ed, remainder of (len - obj.size)]
|
41
|
+
def offset(obj, len)
|
42
|
+
if obj.respond_to? :slice #@todo V should be nil not ''
|
43
|
+
sliced = (obj.slice(0..len-1) || '')
|
44
|
+
[sliced, _offset_from_length(obj, len)]
|
45
|
+
else
|
46
|
+
offset!(_deep_dup(obj), len)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# if the Array contains one item it #pop's it off
|
51
|
+
# otherwise returns the array
|
52
|
+
# @param [Array] ary
|
53
|
+
# @return [Object || Array] [contents of array || the array unchanged
|
54
|
+
def unwrap_ary(ary)
|
55
|
+
(ary.respond_to?(:pop) and ary.one?) ? ary.pop : ary
|
56
|
+
end
|
57
|
+
|
58
|
+
# takes an Array with Ranges inside and checks to see if
|
59
|
+
# the number falls within that or matches one of the Integers passed it
|
60
|
+
# @param [Array<Numeric||Range>] ranged_ary the array to check
|
61
|
+
# @param [Numeric] num the number to check
|
62
|
+
# @return [TrueClass || FalseClass] whethor or not it is in or within that array
|
63
|
+
def in_ranged_array?(ranged_ary, num)
|
64
|
+
!!ranged_ary.find {|n| n === num }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
#
|
70
|
+
module NumericUtils
|
71
|
+
# ceiling division
|
72
|
+
# @param [Integer] num numerator
|
73
|
+
# @param [Integer] denom denominator
|
74
|
+
# @return [Integer]
|
75
|
+
def ceil_div(num, denom)
|
76
|
+
(num.to_f/denom).ceil
|
77
|
+
end
|
78
|
+
|
79
|
+
# Computes the value of the first specified argument
|
80
|
+
# clamped to a range defined by the second argument
|
81
|
+
# @param [Numeric] x the number check against
|
82
|
+
# @param [Hash] min_or_max
|
83
|
+
# @option min_or_max [Numeric] :min lower bounds
|
84
|
+
# @option min_or_max [Numeric] :max upper bounds
|
85
|
+
# @return [Numeric] the clamped number
|
86
|
+
def clamp(x , min_or_max={})
|
87
|
+
[[x, min_or_max[:max]].compact.min, min_or_max[:min]].compact.max
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
#
|
93
|
+
module GemUtils
|
94
|
+
class << self
|
95
|
+
|
96
|
+
#
|
97
|
+
# @return [String] directory of the data folder for this gem
|
98
|
+
def data_dir(file_or_dir_path)
|
99
|
+
"#{gem_path(file_or_dir_path)}#{File::SEPARATOR}data"
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# @return [String]
|
104
|
+
def gem_path(file_or_dir_path)
|
105
|
+
gem_root = Gem.path.find_result do |path|
|
106
|
+
regex = %r<(#{path})(#{File::SEPARATOR}gems#{File::SEPARATOR})>
|
107
|
+
if file_or_dir_path =~ regex
|
108
|
+
paths = file_or_dir_path.split(regex).reject(&:empty?)
|
109
|
+
if paths.size > 2
|
110
|
+
"#{paths[0]}#{paths[1]}#{paths[2].split('/', 2).first}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
if gem_root
|
115
|
+
gem_root
|
116
|
+
else
|
117
|
+
require 'bundler'
|
118
|
+
Bundler.root.to_path
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
#
|
128
|
+
module KernelUtils
|
129
|
+
# @todo change name?
|
130
|
+
# gets the class if an object
|
131
|
+
# and returns self if already a Class
|
132
|
+
# @param obj object to get Class from
|
133
|
+
# @return [Class]
|
134
|
+
def get_class(obj)
|
135
|
+
obj.is_a?(Class) ? obj : obj.class
|
136
|
+
end
|
137
|
+
|
138
|
+
# sends all the args given to it, or none based on #arity the method object passed in
|
139
|
+
# this works great for sending arguments to a list of procs
|
140
|
+
# @param [#call] method_or_proc
|
141
|
+
# @param [Array] args
|
142
|
+
# @param [Proc] block
|
143
|
+
# @return
|
144
|
+
def send_all_or_no_args(callable, *args, &block)
|
145
|
+
callable.call(*[nil, args][clamp(callable.arity, min: 0, max: 1)], &block)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
#
|
151
|
+
module ObjectUtils
|
152
|
+
# checks if object responds to deep_dup first
|
153
|
+
# :deep_dup, :_dump :_dump_data
|
154
|
+
# @todo name change
|
155
|
+
# @param obj
|
156
|
+
# @return
|
157
|
+
def _deep_dup(obj)
|
158
|
+
if obj.respond_to?(:deep_dup) and
|
159
|
+
not method(:deep_dup).source_location.first =~ /activesupport/
|
160
|
+
obj.deep_dup
|
161
|
+
else
|
162
|
+
deep_dup(obj)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# :deep_dup, :_dump :_dump_data
|
167
|
+
# @param obj
|
168
|
+
def deep_dup(obj)
|
169
|
+
Marshal.load(Marshal.dump(obj))
|
170
|
+
end
|
171
|
+
|
172
|
+
# emulates the id in inspect
|
173
|
+
# @param [#object_id]
|
174
|
+
# @return [String]
|
175
|
+
def inspect_id(obj)
|
176
|
+
"0x#{(obj.object_id << 1).to_s(16)}"
|
177
|
+
end
|
178
|
+
|
179
|
+
# @todo test
|
180
|
+
# @param
|
181
|
+
# @param [Proc] block
|
182
|
+
# @return [Array], ['#<', instance variables split by '=', '>']
|
183
|
+
def inspect_shell(obj, &block)
|
184
|
+
['#<', *inspect_id(obj), obj.instance_variables.map do |k, v|
|
185
|
+
ary = [k, '=', v]
|
186
|
+
yield *ary if block_given?
|
187
|
+
ary
|
188
|
+
end, '>']
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
#
|
193
|
+
# simple utils/standard lib type stuff
|
194
|
+
module Utils
|
195
|
+
|
196
|
+
include ArrayAndStringUtils
|
197
|
+
|
198
|
+
include NumericUtils
|
199
|
+
|
200
|
+
include GemUtils
|
201
|
+
|
202
|
+
include KernelUtils
|
203
|
+
|
204
|
+
include ObjectUtils
|
205
|
+
|
206
|
+
extend self
|
207
|
+
|
208
|
+
module_function
|
209
|
+
|
210
|
+
#
|
211
|
+
# @param [#size || #length] obj
|
212
|
+
# @param [Integer] num
|
213
|
+
# @return [Integer || nil] remainder of num if available
|
214
|
+
def _offset_from_length(obj, num)
|
215
|
+
if obj.respond_to?(:length) || obj.respond_to?(:size)
|
216
|
+
clamp(num - (obj.respond(:length) || obj.respond(:size)), min: 0)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
extend Support::GemUtils
|
223
|
+
end
|
224
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,728 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'vigilem/ffi'
|
4
|
+
|
5
|
+
describe Vigilem::FFI::ArrayPointerSync do
|
6
|
+
|
7
|
+
def ary_type
|
8
|
+
:uint
|
9
|
+
end
|
10
|
+
|
11
|
+
before :all do
|
12
|
+
InitLessHost = Class.new do
|
13
|
+
def self.ary_type
|
14
|
+
:uint
|
15
|
+
end
|
16
|
+
include Vigilem::FFI::ArrayPointerSync
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:init_less_host) { InitLessHost.new }
|
21
|
+
|
22
|
+
context '::included' do
|
23
|
+
it 'will set the native_type to POINTER' do
|
24
|
+
expect(InitLessHost.native_type).to eql(FFI::Type::POINTER)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'without initialize_ary_ptr_sync' do
|
29
|
+
context 'point_cuts' do
|
30
|
+
it 'will have only array manipulation methods' do
|
31
|
+
expect(described_class.instance_variable_get(:@point_cuts)).to_not include([
|
32
|
+
*Array.instance_methods.grep(/method|taint|trust|instance_variable|send|class|respond/),
|
33
|
+
:__id__, :object_id, :extend, :freeze, :is_a?, :instance_of? ])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context described_class::ClassMethods do
|
38
|
+
|
39
|
+
it 'will include FFI::DataConverter' do
|
40
|
+
init_less_host.initialize_ary_ptr_sync(5)
|
41
|
+
expect(init_less_host.class).to be_a(FFI::DataConverter)
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '::ary_type' do
|
45
|
+
let(:fail_host) do
|
46
|
+
FailInitLessHost = Class.new do
|
47
|
+
include Vigilem::FFI::ArrayPointerSync
|
48
|
+
self
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'raises NotImplementedError when not overriden' do
|
53
|
+
expect do
|
54
|
+
fail_host.ary_type
|
55
|
+
end.to raise_error(NotImplementedError)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '::ary_type_size' do
|
60
|
+
context 'is a Symbol' do
|
61
|
+
it 'returns the size of ary_type' do
|
62
|
+
expect(init_less_host.class.ary_type_size).to eql(::FFI.find_type(init_less_host.class.ary_type).size)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'is a Class' do
|
67
|
+
|
68
|
+
before do
|
69
|
+
Coord = Class.new(::FFI::Struct) do
|
70
|
+
layout :x, :short,
|
71
|
+
:y, :short
|
72
|
+
self
|
73
|
+
end
|
74
|
+
ClassArrayTypeInitLessHost = Class.new do
|
75
|
+
def self.ary_type
|
76
|
+
Coord
|
77
|
+
end
|
78
|
+
end #Class.new
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'returns the size of ary_type' do
|
82
|
+
expect(init_less_host.class.ary_type_size).to eql(Coord.size)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '::ptr_capacity' do
|
88
|
+
|
89
|
+
let(:pointer) { FFI::MemoryPointer.new(:uint, 3) }
|
90
|
+
|
91
|
+
it 'gets the capacity of the pointer based on ::ary_type_size' do
|
92
|
+
expect(InitLessHost.ptr_capacity(pointer)).to eql(3)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '::ary_of_type' do
|
97
|
+
|
98
|
+
let(:pointer) do
|
99
|
+
poynter = FFI::MemoryPointer.new(:uint, 3)
|
100
|
+
Vigilem::FFI::Utils.put_array_typedef(poynter, :uint, [1, 2, 3])
|
101
|
+
poynter
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'converts a FFI::Pointer to an array_sync of ary_type' do
|
105
|
+
expect(InitLessHost.ary_of_type(pointer)).to eql([1, 2, 3])
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'given a FFI::Struct' do
|
109
|
+
|
110
|
+
class FFIPoints < FFI::Struct
|
111
|
+
layout :x, :uint, :y, :uint
|
112
|
+
end
|
113
|
+
|
114
|
+
let(:points_array) do
|
115
|
+
3.times.map do
|
116
|
+
arg = FFIPoints.new
|
117
|
+
arg[:x] = 1
|
118
|
+
arg[:y] = 2
|
119
|
+
arg
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
let(:points_bytes_array) do
|
124
|
+
points_array.map {|pa| (ptr = pa.to_ptr).read_bytes(ptr.size) }
|
125
|
+
end
|
126
|
+
|
127
|
+
let(:points_pointer) do
|
128
|
+
poynter = FFI::MemoryPointer.new(FFIPoints, 3)
|
129
|
+
poynter.write_bytes(points_bytes_array.join)
|
130
|
+
poynter
|
131
|
+
end
|
132
|
+
|
133
|
+
class FFIHost
|
134
|
+
def self.ary_type
|
135
|
+
FFIPoints
|
136
|
+
end
|
137
|
+
include Vigilem::FFI::ArrayPointerSync
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'converts a FFI::Struct to an array_sync of ary_type' do
|
141
|
+
result = FFIHost.ary_of_type(points_pointer).map.with_index do |ffi_pnt, idx|
|
142
|
+
ffi_pnt.to_ptr.read_bytes(FFIHost.ary_type_size)
|
143
|
+
end
|
144
|
+
|
145
|
+
expect(result).to eql(points_bytes_array)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'given a VFFIStruct' do
|
150
|
+
class VFFIPoints < VFFIStruct
|
151
|
+
layout :x, :uint, :y, :uint
|
152
|
+
end
|
153
|
+
|
154
|
+
let(:points_array) do
|
155
|
+
3.times.map do
|
156
|
+
arg = VFFIPoints.new
|
157
|
+
arg[:x] = 1
|
158
|
+
arg[:y] = 2
|
159
|
+
arg
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
let(:points_pointer) do
|
164
|
+
poynter = FFI::MemoryPointer.new(VFFIPoints, 3)
|
165
|
+
poynter.write_bytes(points_array.map(&:bytes).join)
|
166
|
+
poynter
|
167
|
+
end
|
168
|
+
|
169
|
+
let(:vffi_host) do
|
170
|
+
VFFIHost = Class.new do
|
171
|
+
def self.ary_type
|
172
|
+
VFFIPoints
|
173
|
+
end
|
174
|
+
include Vigilem::FFI::ArrayPointerSync
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'converts it to an array_sync of ary_type' do
|
179
|
+
expect(vffi_host.ary_of_type(points_pointer).map(&:bytes)).to eql(points_array.map(&:bytes))
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
describe '::raise_size_error' do
|
187
|
+
it 'raises NameError "size cannot exceed #{obj.max_size} for #{obj}" ' do
|
188
|
+
expect do
|
189
|
+
Host.raise_size_error
|
190
|
+
end.to raise_error(NameError)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
describe '#initialize_ary_ptr_sync' do
|
199
|
+
|
200
|
+
context 'not called' do
|
201
|
+
it 'will not have initialized the ptr_cache_hash' do
|
202
|
+
expect(init_less_host.send(:ptr_cache_hash)).to be_nil
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'will not have initialized the cache_hash' do
|
206
|
+
expect(init_less_host.send(:cache_hash)).to be_nil
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'will not have initialized the ptr' do
|
210
|
+
expect do
|
211
|
+
init_less_host.ptr
|
212
|
+
end.to raise_error(TypeError)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
context 'called' do
|
216
|
+
|
217
|
+
before(:each) do
|
218
|
+
init_less_host.initialize_ary_ptr_sync(5)
|
219
|
+
end
|
220
|
+
|
221
|
+
let(:init_size) { 5 }
|
222
|
+
|
223
|
+
let(:init_ptr) { FFI::MemoryPointer.new(ary_type, init_size) }
|
224
|
+
|
225
|
+
let(:init_ary) { [1, 2, 3] }
|
226
|
+
|
227
|
+
it 'sets up the ptr cache' do
|
228
|
+
expect(init_less_host.send(:ptr_cache_hash)).to_not be_nil
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'sets up the array cache' do
|
232
|
+
expect(init_less_host.send(:cache_hash)).to_not be_nil
|
233
|
+
end
|
234
|
+
|
235
|
+
context 'with a pointer arg' do
|
236
|
+
|
237
|
+
before(:each) { init_less_host.initialize_ary_ptr_sync(init_ptr) }
|
238
|
+
|
239
|
+
it 'will init the ptr' do
|
240
|
+
expect(init_less_host.ptr).to_not be_nil
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'will get the max_len from the pointer' do
|
244
|
+
expect(init_less_host.max_size).to eql(init_size)
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
|
249
|
+
context 'pointer arg with init_values' do
|
250
|
+
it 'will raise error if given both a pointer and initial values' do
|
251
|
+
expect do
|
252
|
+
init_less_host.initialize_ary_ptr_sync(init_ptr, init_ary)
|
253
|
+
end.to raise_error(ArgumentError)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context 'with a max_len arg' do
|
258
|
+
|
259
|
+
it 'inits max_size' do
|
260
|
+
init_less_host.initialize_ary_ptr_sync(init_size)
|
261
|
+
expect(init_less_host.max_size).to eql(init_size)
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'throws and error when init_size does not respond to #to_i' do
|
265
|
+
expect do
|
266
|
+
init_less_host.initialize_ary_ptr_sync(Object.new)
|
267
|
+
end.to raise_error(TypeError)
|
268
|
+
end
|
269
|
+
|
270
|
+
end
|
271
|
+
context 'with max_len and init_values' do
|
272
|
+
|
273
|
+
before(:each) { init_less_host.initialize_ary_ptr_sync(init_size, *init_ary) }
|
274
|
+
|
275
|
+
it 'sets the max_size' do
|
276
|
+
expect(init_less_host.max_size).to eql(init_size)
|
277
|
+
end
|
278
|
+
|
279
|
+
it 'inserts some starter values' do
|
280
|
+
expect(init_less_host.send(:ary)).to eql(init_ary)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
context 'after init' do
|
288
|
+
|
289
|
+
before :all do
|
290
|
+
Host = Class.new do
|
291
|
+
def self.ary_type
|
292
|
+
:uint
|
293
|
+
end
|
294
|
+
include Vigilem::FFI::ArrayPointerSync
|
295
|
+
def initialize(max_len_or_ptr, *init_values)
|
296
|
+
initialize_ary_ptr_sync(max_len_or_ptr, *init_values)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
let(:host) { Host.new(3) }
|
302
|
+
|
303
|
+
describe '#after_ary_method' do
|
304
|
+
let(:array_intercept_host) do
|
305
|
+
ArrayInterceptInitLessHost = Class.new do
|
306
|
+
def self.ary_type
|
307
|
+
:uint
|
308
|
+
end
|
309
|
+
include Vigilem::FFI::ArrayPointerSync
|
310
|
+
def after_ary_method(method_name, return_value, *args, &block)
|
311
|
+
'HAHA! not the value you wanted'
|
312
|
+
end
|
313
|
+
end
|
314
|
+
ArrayInterceptInitLessHost.new.initialize_ary_ptr_sync(20)
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'is executed after array methods' do
|
318
|
+
expect(array_intercept_host << 1).to eql('HAHA! not the value you wanted')
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
describe '#max_size, #max_size=' do
|
323
|
+
let(:len) { 1 }
|
324
|
+
|
325
|
+
it 'max_size will be updated' do
|
326
|
+
host.send(:max_size=, len)
|
327
|
+
expect(host.max_size).to eql(len)
|
328
|
+
end
|
329
|
+
|
330
|
+
it 'max_len will be the same as max_size' do
|
331
|
+
host.send(:max_size=, len)
|
332
|
+
expect(host.max_len).to eql(host.max_size)
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
describe '#max_len, #max_len=' do
|
337
|
+
let(:len) { 1 }
|
338
|
+
|
339
|
+
it 'max_len will be updated' do
|
340
|
+
host.send(:max_len=, len)
|
341
|
+
expect(host.max_len).to eql(len)
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'max_size will be the same as max_len' do
|
345
|
+
host.send(:max_len=, len)
|
346
|
+
expect(host.max_size).to eql(host.max_len)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
describe '#replace' do
|
351
|
+
# @todo
|
352
|
+
#let!(:host) { host.concat([1, 2]) } # so this fails silently
|
353
|
+
|
354
|
+
it 'will work just like Array#replace' do
|
355
|
+
expect(host.replace([3, 4]).to_a).to eql([3,4])
|
356
|
+
end
|
357
|
+
end
|
358
|
+
describe '#bytes' do
|
359
|
+
it 'reads all the bytes from the pointer' do
|
360
|
+
host.concat([1, 2])
|
361
|
+
empty = "\x00" * (host.ptr.type_size - 1)
|
362
|
+
expect(host.bytes).to eql("\x01#{empty}\x02#{empty}\x00#{empty}")
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
describe '::bytes_of' do
|
367
|
+
it 'gets the bytes of an item in the array specified by index' do
|
368
|
+
host.concat([1, 2])
|
369
|
+
empty = "\x00" * (host.ptr.type_size - 1)
|
370
|
+
expect(host.bytes_of(1)).to eql("\x02#{empty}")
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
describe '#offsets' do
|
375
|
+
|
376
|
+
it %q(returns the offset of the objects in the array on the pointer it's in) do
|
377
|
+
host.concat([1, 2, 4])
|
378
|
+
len = host.ptr.type_size
|
379
|
+
expect(host.offsets).to eql([0, len, len *2])
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
context 'private' do
|
384
|
+
|
385
|
+
describe '#ary' do
|
386
|
+
it 'defaults to empty' do
|
387
|
+
expect(host.send(:ary)).to eql([])
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
describe '#_size' do
|
392
|
+
let(:part_dble_host) do
|
393
|
+
allow(host).to receive(:array).and_call_original
|
394
|
+
allow(host).to receive(:update)
|
395
|
+
allow(host).to receive(:_size).and_call_original
|
396
|
+
host.send(:ary).concat([1, 2, 3])
|
397
|
+
host
|
398
|
+
end
|
399
|
+
|
400
|
+
it 'returns the size' do
|
401
|
+
expect(part_dble_host.send(:_size)).to eql(3)
|
402
|
+
end
|
403
|
+
|
404
|
+
it %q(won't call :update) do
|
405
|
+
expect(part_dble_host).to_not have_received(:update)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
describe '#ptr_hash' do
|
410
|
+
it 'creates a hash for the values in the ptr' do
|
411
|
+
expect(host.send(:ptr_hash)).to eql(host.ptr.read_bytes(host.ptr.size).hash)
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
describe '#ptr_changed?' do
|
416
|
+
|
417
|
+
context 'nothing changed' do
|
418
|
+
it 'will return false when the pointer has not changed' do
|
419
|
+
expect(host.send(:ptr_changed?)).to be_falsey
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
context 'ptr value changed' do
|
424
|
+
it %q(will return true when the pointer value changed and update hasn't ran) do
|
425
|
+
Vigilem::FFI::Utils.write_array_typedef(host.ptr, Host.ary_type, [1,2])
|
426
|
+
expect(host.send(:ptr_changed?)).to be_truthy
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
end
|
431
|
+
|
432
|
+
describe '#update_ptr_cache' do
|
433
|
+
it 'updates the cache of the pointer with the new pointer value' do
|
434
|
+
old_hsh = host.send(:ptr_cache_hash)
|
435
|
+
Vigilem::FFI::Utils.write_array_typedef(host.ptr, Host.ary_type, [1,2])
|
436
|
+
expect(host.send(:update_ptr_cache)).not_to eql(old_hsh)
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
describe '#update_ptr' do
|
441
|
+
|
442
|
+
let(:input) { [1, 2, 3] }
|
443
|
+
|
444
|
+
it 'updates the ptr from the #ary values' do
|
445
|
+
host.send(:ary).concat(input)
|
446
|
+
expect(host.bytes).to eql(input.pack('L*'))
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
describe '#ary_changed?' do
|
451
|
+
context 'nothing changed' do
|
452
|
+
it 'will return false when the ary has not changed' do
|
453
|
+
expect(host.send(:ary_changed?)).to be_falsey
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
context 'ary value changed' do
|
458
|
+
it %q(will return true when the ary value changed and update hasn't ran) do
|
459
|
+
host.send(:ary) << 1
|
460
|
+
expect(host.send(:ary_changed?)).to be_truthy
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
describe 'update_ary_cache' do
|
466
|
+
it 'updates the cache of the ary with the new pointer value' do
|
467
|
+
old_hsh = host.send(:cache_hash)
|
468
|
+
host.send(:ary).concat([1,2,3])
|
469
|
+
expect(host.send(:update_ary_cache)).not_to eql(old_hsh)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
describe '#update_ary' do
|
474
|
+
|
475
|
+
let(:input) { [1, 2, 3] }
|
476
|
+
|
477
|
+
it 'updates the #ary from the #ptr values' do
|
478
|
+
Vigilem::FFI::Utils.write_array_typedef(host.ptr, Host.ary_type, input)
|
479
|
+
host.send(:update_ary)
|
480
|
+
expect(host.send(:ary)).to eql(input)
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
describe '#update_cache' do
|
485
|
+
it 'updates both caches' do
|
486
|
+
old_caches = [host.send(:cache_hash), host.send(:ptr_cache_hash)]
|
487
|
+
host.send(:ary).concat([1,2,3])
|
488
|
+
Vigilem::FFI::Utils.write_array_typedef(host.ptr, Host.ary_type, [1,2])
|
489
|
+
expect(host.send(:update_cache)).not_to eql(old_caches)
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
describe '#update' do
|
494
|
+
let(:input) { [1, 2, 3] }
|
495
|
+
|
496
|
+
it 'updates the ptr if the ary was updated' do
|
497
|
+
host.send(:ary).concat(input)
|
498
|
+
host.send(:update)
|
499
|
+
expect((pt = host.send(:ptr)).read_bytes(pt.size)).to eql(input.pack('L*'))
|
500
|
+
end
|
501
|
+
|
502
|
+
it 'updates the ary if the ptr was updated' do
|
503
|
+
Vigilem::FFI::Utils.write_array_typedef(host.ptr, Host.ary_type, [1,2,3])
|
504
|
+
host.send(:update)
|
505
|
+
expect(host.send(:ary)).to eql(input)
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
describe '#raise_size_error' do
|
510
|
+
it 'raises size error' do
|
511
|
+
expect do
|
512
|
+
host.send(:_raise_size_error)
|
513
|
+
end.to raise_error(Vigilem::Support::MaxSizeError)
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
describe '#_ptr_offsets' do
|
518
|
+
it 'returns an array of offsets based on the size of the array and type size of the elements' do
|
519
|
+
host.send(:ary).concat([1,2,3,4])
|
520
|
+
expect(host.send(:_ptr_offsets)).to eql([0, 4, 8, 12])
|
521
|
+
end
|
522
|
+
end
|
523
|
+
end # private
|
524
|
+
|
525
|
+
# @todo change some of the wording
|
526
|
+
describe '#what_changed? and #changed?,' do
|
527
|
+
|
528
|
+
context 'nothing changed,' do
|
529
|
+
it 'then returns all false' do
|
530
|
+
expect(host.what_changed?.values.all?).to be_falsey
|
531
|
+
end
|
532
|
+
|
533
|
+
it 'then changed? is false' do
|
534
|
+
expect(host.changed?).to be_falsey
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
context 'ary_changed?' do
|
539
|
+
before(:each) { host.send(:ary) << 1 }
|
540
|
+
|
541
|
+
it 'then array => true' do
|
542
|
+
expect(host.what_changed?).to eql({ary: true, ptr: false})
|
543
|
+
end
|
544
|
+
|
545
|
+
it 'then changed? is true' do
|
546
|
+
expect(host.changed?).to be_truthy
|
547
|
+
end
|
548
|
+
end
|
549
|
+
context 'ptr_changed?' do
|
550
|
+
before(:each) { Vigilem::FFI::Utils.write_array_typedef(host.ptr, Host.ary_type, [1,2]) }
|
551
|
+
|
552
|
+
it 'then :ptr => true' do
|
553
|
+
expect(host.what_changed?).to eql({ary: false, ptr: true})
|
554
|
+
end
|
555
|
+
|
556
|
+
it 'then changed? is true' do
|
557
|
+
expect(host.changed?).to be_truthy
|
558
|
+
end
|
559
|
+
end
|
560
|
+
context 'both changed' do
|
561
|
+
it 'then raises an error' do
|
562
|
+
host.send(:ary) << 1
|
563
|
+
Vigilem::FFI::Utils.write_array_typedef(host.ptr, Host.ary_type, [1, 2])
|
564
|
+
expect do
|
565
|
+
host.what_changed?
|
566
|
+
end.to raise_error(RuntimeError)
|
567
|
+
end
|
568
|
+
|
569
|
+
it 'then changed? is raises error' do
|
570
|
+
host.send(:ary) << 1
|
571
|
+
Vigilem::FFI::Utils.write_array_typedef(host.ptr, Host.ary_type, [1, 2])
|
572
|
+
expect do
|
573
|
+
host.changed?
|
574
|
+
end.to raise_error(RuntimeError)
|
575
|
+
end
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
describe '#out_of_bounds?' do
|
580
|
+
it 'returns false when array.size <= max_size' do
|
581
|
+
host.send(:ary).concat([1])
|
582
|
+
expect(host.out_of_bounds?).to be_falsey
|
583
|
+
end
|
584
|
+
it 'returns true when array.size > max_size' do
|
585
|
+
host.send(:ary).concat([1, 2, 3, 4, 5])
|
586
|
+
expect(host.out_of_bounds?).to be_truthy
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
describe 'out_of_bounds_check' do
|
591
|
+
it 'returns nil if not out of bounds' do
|
592
|
+
host.send(:ary).concat([1])
|
593
|
+
expect(host.out_of_bounds_check).to be_nil
|
594
|
+
end
|
595
|
+
it 'raises error when out of bounds' do
|
596
|
+
host.send(:ary).concat([1, 2, 3, 4, 5])
|
597
|
+
expect do
|
598
|
+
host.out_of_bounds_check
|
599
|
+
end.to raise_error(Vigilem::Support::MaxSizeError)
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
describe '#to_s' do
|
604
|
+
it 'resembles an array' do
|
605
|
+
host << 1
|
606
|
+
expect(host.to_s).to eql("[1]")
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
describe '#to_a' do
|
611
|
+
it 'gets the array representation of the ary_sync' do
|
612
|
+
expect(host.to_a).to eql(host.send(:ary))
|
613
|
+
end
|
614
|
+
end
|
615
|
+
|
616
|
+
describe '#dup' do
|
617
|
+
before(:each) do
|
618
|
+
host.concat([1, 2, 3])
|
619
|
+
end
|
620
|
+
let(:dup) { host.dup }
|
621
|
+
it 'duplicates the values of ary_ptr_sync' do
|
622
|
+
expect(host.send(:ary)).to eql(dup.send(:ary))
|
623
|
+
end
|
624
|
+
|
625
|
+
it 'creates a new object' do
|
626
|
+
expect(host.object_id).not_to eql(dup.object_id)
|
627
|
+
end
|
628
|
+
|
629
|
+
end
|
630
|
+
|
631
|
+
describe '#pop' do
|
632
|
+
context 'pointer array' do
|
633
|
+
|
634
|
+
before :all do
|
635
|
+
class Pnt < ::FFI::Struct
|
636
|
+
layout :x, :long,
|
637
|
+
:y, :long
|
638
|
+
|
639
|
+
class << self
|
640
|
+
def native_type
|
641
|
+
::FFI::Pointer
|
642
|
+
end
|
643
|
+
|
644
|
+
def to_native(value, ctx)
|
645
|
+
value.to_ptr
|
646
|
+
end
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|
650
|
+
class PointAry
|
651
|
+
def self.ary_type
|
652
|
+
Pnt
|
653
|
+
end
|
654
|
+
include Vigilem::FFI::ArrayPointerSync
|
655
|
+
def initialize(max_len_or_ptr, *init_values)
|
656
|
+
initialize_ary_ptr_sync(max_len_or_ptr, *init_values)
|
657
|
+
end
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
let(:points) do
|
662
|
+
0.upto(5).each_slice(2).map do |n, n_1|
|
663
|
+
obj = Pnt.new
|
664
|
+
obj[:x] = n
|
665
|
+
obj[:y] = n_1
|
666
|
+
obj
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
let(:point_ary) do
|
671
|
+
PointAry.new(3)
|
672
|
+
end
|
673
|
+
|
674
|
+
before(:each) do
|
675
|
+
point_ary.concat(points)
|
676
|
+
end
|
677
|
+
|
678
|
+
it 'removes an item from the stack' do
|
679
|
+
expect(point_ary.pop).to be_a(Pnt) and have_attributes( x: 4, y: 5 )
|
680
|
+
end
|
681
|
+
|
682
|
+
it 'will not be Hash equal to an array' do
|
683
|
+
point_ary.pop
|
684
|
+
expect(point_ary).not_to eql(points[0..1])
|
685
|
+
end
|
686
|
+
|
687
|
+
it 'will modify the original array' do
|
688
|
+
point_ary.pop
|
689
|
+
expect(point_ary).to be_an(PointAry) and match [
|
690
|
+
an_object_having_attributes( x: 0, y: 1 ),
|
691
|
+
an_object_having_attributes( x: 2, y: 3 )
|
692
|
+
]
|
693
|
+
end
|
694
|
+
end
|
695
|
+
|
696
|
+
context 'simple array' do
|
697
|
+
before(:each) do
|
698
|
+
host.concat([1, 2, 3])
|
699
|
+
end
|
700
|
+
|
701
|
+
it 'removes an item from the stack' do
|
702
|
+
expect(host.pop).to eql(3)
|
703
|
+
end
|
704
|
+
|
705
|
+
it 'will not be Hash equal to an array' do
|
706
|
+
host.pop
|
707
|
+
expect(host).not_to eql([1, 2])
|
708
|
+
end
|
709
|
+
|
710
|
+
it 'will modify the original array' do
|
711
|
+
host.pop
|
712
|
+
expect(host).to be == [1,2]
|
713
|
+
end
|
714
|
+
end
|
715
|
+
end
|
716
|
+
|
717
|
+
describe '#inspect' do
|
718
|
+
it 'produces a string with all variables' do
|
719
|
+
expect(host.inspect).to match(
|
720
|
+
/#<[A-Z][a-z\d]+:0x[a-z\d]+ \[.*\] @max_size=\d+ @ptr=#<FFI::MemoryPointer address=0x[a-z\d]+ size=\d+> @cache_hash=-?\d+ @ptr_cache_hash=-?\d+>/
|
721
|
+
)
|
722
|
+
end
|
723
|
+
|
724
|
+
end
|
725
|
+
|
726
|
+
end #after_init
|
727
|
+
|
728
|
+
end
|