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.
- 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
|