snow-data 1.0.0 → 1.1.0
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 +4 -4
- data/ext/extconf.rb +3 -0
- data/ext/snow-data/snow-data.c +147 -29
- data/lib/snow-data/c_struct.rb +145 -371
- data/lib/snow-data/c_struct/array_base.rb +164 -0
- data/lib/snow-data/c_struct/struct_base.rb +304 -0
- data/lib/snow-data/memory.rb +14 -4
- data/lib/snow-data/version.rb +1 -1
- metadata +4 -2
@@ -0,0 +1,164 @@
|
|
1
|
+
# This file is part of ruby-snowdata.
|
2
|
+
# Copyright (c) 2013 Noel Raymond Cower. All rights reserved.
|
3
|
+
# See COPYING for license details.
|
4
|
+
|
5
|
+
require 'snow-data/memory'
|
6
|
+
|
7
|
+
|
8
|
+
module Snow ; end
|
9
|
+
|
10
|
+
class Snow::CStruct ; end
|
11
|
+
|
12
|
+
|
13
|
+
#
|
14
|
+
# Array base for struct type arrays. Provides fetch/store and allocators.
|
15
|
+
#
|
16
|
+
# Fetch and store operations both depend on an internal cache of wrapper Memory
|
17
|
+
# objects that point to structs in the array.
|
18
|
+
#
|
19
|
+
module Snow::CStruct::StructArrayBase
|
20
|
+
|
21
|
+
module Allocators ; end
|
22
|
+
|
23
|
+
|
24
|
+
def self.included(array_klass)
|
25
|
+
array_klass.extend(Allocators)
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
include Enumerable
|
30
|
+
|
31
|
+
|
32
|
+
# The length of the array.
|
33
|
+
attr_reader :length
|
34
|
+
|
35
|
+
|
36
|
+
def resize!(new_length) # :nodoc:
|
37
|
+
raise ArgumentError, "Length must be greater than zero" if new_length < 1
|
38
|
+
realloc!(new_length * self.class::BASE::SIZE, self.class::BASE::ALIGNMENT)
|
39
|
+
@length = new_length
|
40
|
+
__free_cache__
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def each(&block) # :nodoc:
|
46
|
+
return to_enum(:each) unless block_given?
|
47
|
+
(0 ... self.length).each { |index| yield fetch(index) }
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def map(&block) # :nodoc:
|
53
|
+
return to_enum(:map) unless block_given?
|
54
|
+
self.dup.map!(&block)
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def map!(&block) # :nodoc:
|
59
|
+
return to_enum(:map!) unless block_given?
|
60
|
+
(0 ... self.length).each { |index| store(index, yield(fetch(index))) }
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def to_a # :nodoc:
|
66
|
+
(0 ... self.length).map { |index| fetch(index) }
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def fetch(index) # :nodoc:
|
71
|
+
raise RuntimeError, "Attempt to access deallocated array" if @length == 0
|
72
|
+
raise RangeError, "Attempt to access out-of-bounds index in #{self.class}" if index < 0 || @length <= index
|
73
|
+
__build_cache__ if ! @__cache__
|
74
|
+
@__cache__[index]
|
75
|
+
end
|
76
|
+
alias_method :[], :fetch
|
77
|
+
|
78
|
+
|
79
|
+
#
|
80
|
+
# You can use this to assign _any_ Data subclass to an array value, but
|
81
|
+
# keep in mind that the data assigned MUST -- again, MUST -- be at least
|
82
|
+
# as large as the array's base struct type in bytes or the assigned
|
83
|
+
# data object MUST respond to a bytesize message to get its size in
|
84
|
+
# bytes.
|
85
|
+
#
|
86
|
+
def store(index, data) # :nodoc:
|
87
|
+
raise RuntimeError, "Attempt to access deallocated array" if @length == 0
|
88
|
+
raise TypeError, "Invalid value type, must be Data, but got #{data.class}" if ! data.kind_of?(Data)
|
89
|
+
raise RangeError, "Attempt to access out-of-bounds index in #{self.class}" if index < 0 || @length <= index
|
90
|
+
@__cache__[index].copy!(data)
|
91
|
+
data
|
92
|
+
end
|
93
|
+
alias_method :[]=, :store
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
def free! # :nodoc:
|
98
|
+
__free_cache__
|
99
|
+
@length = 0
|
100
|
+
super
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def __free_cache__ # :nodoc:
|
107
|
+
if @__cache__
|
108
|
+
@__cache__.each { |entry|
|
109
|
+
entry.free!
|
110
|
+
entry.remove_instance_variable(:@__base_memory__)
|
111
|
+
} # zeroes address, making it NULL
|
112
|
+
@__cache__ = nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
def __build_cache__ # :nodoc:
|
118
|
+
addr = self.address
|
119
|
+
@__cache__ = (0...length).map { |index|
|
120
|
+
wrapper = self.class::BASE.__wrap__(addr + index * self.class::BASE::SIZE, self.class::BASE::SIZE)
|
121
|
+
# Make sure the wrapped object keeps the memory from being collected while it's in use
|
122
|
+
wrapper.instance_variable_set(:@__base_memory__, self)
|
123
|
+
wrapper
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
end # module StructArrayBase
|
128
|
+
|
129
|
+
|
130
|
+
|
131
|
+
module Snow::CStruct::StructArrayBase::Allocators
|
132
|
+
|
133
|
+
def wrap(address, length_in_elements) # :nodoc:
|
134
|
+
__wrap__(address, length_in_elements * self::BASE::SIZE)
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
def new(length) # :nodoc:
|
139
|
+
length = length.to_i
|
140
|
+
raise ArgumentError, "Length must be greater than zero" if length < 1
|
141
|
+
inst = __malloc__(length * self::BASE::SIZE, self::BASE::ALIGNMENT)
|
142
|
+
inst.instance_variable_set(:@length, length)
|
143
|
+
inst.instance_variable_set(:@__cache__, nil)
|
144
|
+
inst
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
if ::Snow::Memory::HAS_ALLOCA
|
149
|
+
def alloca(length, &block)
|
150
|
+
__alloca__(length * self::BASE::SIZE) {
|
151
|
+
|mem|
|
152
|
+
mem.instance_variable_set(:@length, length)
|
153
|
+
mem.instance_variable_set(:@__cache__, nil)
|
154
|
+
yield(mem)
|
155
|
+
}
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
alias_method :[], :new
|
161
|
+
|
162
|
+
end # module Allocators
|
163
|
+
|
164
|
+
|
@@ -0,0 +1,304 @@
|
|
1
|
+
# This file is part of ruby-snowdata.
|
2
|
+
# Copyright (c) 2013 Noel Raymond Cower. All rights reserved.
|
3
|
+
# See COPYING for license details.
|
4
|
+
|
5
|
+
require 'snow-data/memory'
|
6
|
+
|
7
|
+
|
8
|
+
module Snow ; end
|
9
|
+
|
10
|
+
class Snow::CStruct ; end
|
11
|
+
|
12
|
+
|
13
|
+
#
|
14
|
+
# Struct base class. Not to be used directly, as it does not provide all the
|
15
|
+
# constants and methods necessary for a struct.
|
16
|
+
#
|
17
|
+
module Snow::CStruct::StructBase
|
18
|
+
|
19
|
+
module Allocators ; end
|
20
|
+
module MemberInfoSupport ; end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Returns the address of a member. This address is only valid for the
|
24
|
+
# receiver.
|
25
|
+
#
|
26
|
+
def address_of(member)
|
27
|
+
self.address + self.class.offset_of(member)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
#
|
32
|
+
# Returns the offset of a member.
|
33
|
+
#
|
34
|
+
def offset_of(member)
|
35
|
+
self.class.offset_of(member)
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
#
|
40
|
+
# Returns the size in bytes of a member.
|
41
|
+
#
|
42
|
+
def bytesize_of(member)
|
43
|
+
self.class.bytesize_of(member)
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
#
|
48
|
+
# Returns the alignment of a member.
|
49
|
+
#
|
50
|
+
def alignment_of(member)
|
51
|
+
self.class.alignment_of(member)
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
#
|
56
|
+
# Returns the length of a member.
|
57
|
+
#
|
58
|
+
def length_of(member)
|
59
|
+
self.class.length_of(member)
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
#
|
64
|
+
# Returns the type name of a member.
|
65
|
+
#
|
66
|
+
def type_of(member)
|
67
|
+
self.class.type_of(member)
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
#
|
72
|
+
# Returns a hash of all of the struct's member names to their values.
|
73
|
+
#
|
74
|
+
def to_h
|
75
|
+
self.class::MEMBERS.inject({}) do |hash, member|
|
76
|
+
length = member.length
|
77
|
+
name = member.name
|
78
|
+
|
79
|
+
hash[name] = if length > 1
|
80
|
+
(0 ... length).map { |index| __send__(name, index) }
|
81
|
+
else
|
82
|
+
__send__(name)
|
83
|
+
end
|
84
|
+
|
85
|
+
hash
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
#
|
91
|
+
# Returns a string describing the struct, including its classname, object
|
92
|
+
# ID, address, size, and alignment. In addition, if long_inspect is enabled,
|
93
|
+
# then it will also include the values of the struct's members.
|
94
|
+
#
|
95
|
+
def inspect
|
96
|
+
id_text = __id__.to_s(16).rjust(14, ?0)
|
97
|
+
addr_text = self.address.to_s(16).rjust(14, ?0)
|
98
|
+
|
99
|
+
member_text = if ! null? && CStruct.long_inspect
|
100
|
+
# Get member text
|
101
|
+
all_members_text = (self.class::MEMBERS.map do |member|
|
102
|
+
name = member.name
|
103
|
+
type = member.type
|
104
|
+
align = member.alignment
|
105
|
+
length = member.length
|
106
|
+
length_decl = length > 1 ? "[#{length}]" : ''
|
107
|
+
|
108
|
+
values_text = if length > 1
|
109
|
+
single_member_text = (0 ... length).map { |index|
|
110
|
+
"[#{ index }]=#{ self.__send__(member.name, index).inspect }"
|
111
|
+
}.join(', ')
|
112
|
+
|
113
|
+
"{ #{ single_member_text } }"
|
114
|
+
else
|
115
|
+
self.__send__(member.name).inspect
|
116
|
+
end
|
117
|
+
|
118
|
+
"#{ name }:#{ type }#{ length_decl }:#{ align }=#{ values_text }"
|
119
|
+
|
120
|
+
end).join('; ')
|
121
|
+
|
122
|
+
" #{all_members_text}"
|
123
|
+
else # member_text = if ...
|
124
|
+
# Skip members
|
125
|
+
''
|
126
|
+
end # member_text = if ...
|
127
|
+
|
128
|
+
"<#{ self.class }:0x#{ id_text } *0x#{ addr_text }:#{ self.bytesize }:#{ self.alignment }#{member_text}>"
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
#
|
133
|
+
# Gets the value of the member with the given name and index.
|
134
|
+
#
|
135
|
+
def [](name, index = 0)
|
136
|
+
__send__(self.class::MEMBERS_GETFN[name], index)
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
#
|
141
|
+
# Sets the value of the member with the given name and index.
|
142
|
+
#
|
143
|
+
def []=(name, index = 0, value)
|
144
|
+
__send__(self.class::MEMBERS_SETFN[name], value, index)
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
def self.define_member_methods(struct_klass)
|
149
|
+
struct_klass.class_exec do
|
150
|
+
self::MEMBERS.each do |member|
|
151
|
+
|
152
|
+
name = member.name
|
153
|
+
index_range = (0...member.length)
|
154
|
+
type_name = member.type
|
155
|
+
type_size = ::Snow::CStruct::SIZES[member.type]
|
156
|
+
offset = member.offset
|
157
|
+
getter = :"get_#{type_name}"
|
158
|
+
setter = :"set_#{type_name}"
|
159
|
+
get_name = :"get_#{name}"
|
160
|
+
set_name = :"set_#{name}"
|
161
|
+
|
162
|
+
define_method(get_name) do |index = 0|
|
163
|
+
if index === index_range
|
164
|
+
raise RangeError, "Index #{index} for #{name} is out of range: must be in #{index_range}"
|
165
|
+
end
|
166
|
+
off = offset + index * type_size
|
167
|
+
__send__(getter, off)
|
168
|
+
end # get_name
|
169
|
+
|
170
|
+
|
171
|
+
define_method(set_name) do |value, index = 0|
|
172
|
+
if index === index_range
|
173
|
+
raise RangeError, "Index #{index} for #{name} is out of range: must be in #{index_range}"
|
174
|
+
end
|
175
|
+
off = offset + index * type_size
|
176
|
+
__send__(setter, off, value)
|
177
|
+
value
|
178
|
+
end # set_name
|
179
|
+
|
180
|
+
|
181
|
+
alias_method :"#{name}", get_name
|
182
|
+
alias_method :"#{name}=", set_name
|
183
|
+
|
184
|
+
extend MemberInfoSupport
|
185
|
+
|
186
|
+
end # self::MEMBERS.each
|
187
|
+
end # self.class_exec
|
188
|
+
end # define_member_methods!
|
189
|
+
|
190
|
+
|
191
|
+
#
|
192
|
+
# Upon inclusion, defines the given struct_klass's member accessors/mutators
|
193
|
+
# as well as its allocator functions.
|
194
|
+
#
|
195
|
+
def self.included(struct_klass)
|
196
|
+
struct_klass.extend(Allocators)
|
197
|
+
struct_klass.extend(MemberInfoSupport)
|
198
|
+
define_member_methods(struct_klass)
|
199
|
+
end # included
|
200
|
+
|
201
|
+
end # module StructBase
|
202
|
+
|
203
|
+
|
204
|
+
#
|
205
|
+
# Allocator methods for struct types defined through CStruct. Namely structs'
|
206
|
+
# ::new, ::wrap, and ::[] (new array) class methods.
|
207
|
+
#
|
208
|
+
module Snow::CStruct::StructBase::Allocators
|
209
|
+
|
210
|
+
#
|
211
|
+
# call-seq:
|
212
|
+
# new { |struct| ... } => new_struct
|
213
|
+
# new => new_struct
|
214
|
+
#
|
215
|
+
# Allocates a new struct and returns it. If a block is given, the new struct
|
216
|
+
# is first yielded to the block then returned. You may use this to initialize
|
217
|
+
# the block or do whatever else you like with it.
|
218
|
+
#
|
219
|
+
def new(&block)
|
220
|
+
inst = __malloc__(self::SIZE, self::ALIGNMENT)
|
221
|
+
yield(inst) if block_given?
|
222
|
+
inst
|
223
|
+
end
|
224
|
+
|
225
|
+
if ::Snow::Memory::HAS_ALLOCA
|
226
|
+
def alloca(&block)
|
227
|
+
__alloca__(self::SIZE, &block)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
#
|
232
|
+
# Returns a struct object that wraps an existing memory address. The returned
|
233
|
+
# object does not own the memory associated with the address, and as such the
|
234
|
+
# wrapped memory may be subject to deallocation at any time, either by the
|
235
|
+
# garbage collector or otherwise, if not kept around somehow.
|
236
|
+
#
|
237
|
+
def wrap(address, alignment = self::ALIGNMENT)
|
238
|
+
__wrap__(address, self::SIZE, alignment)
|
239
|
+
end
|
240
|
+
|
241
|
+
#
|
242
|
+
# call-seq:
|
243
|
+
# Struct[length] => Struct::Array
|
244
|
+
#
|
245
|
+
# Allocates an array of structs with the requested length.
|
246
|
+
#
|
247
|
+
def [](length)
|
248
|
+
self::Array.new(length)
|
249
|
+
end
|
250
|
+
|
251
|
+
end # module Allocators
|
252
|
+
|
253
|
+
|
254
|
+
|
255
|
+
module Snow::CStruct::StructBase::MemberInfoSupport
|
256
|
+
|
257
|
+
#
|
258
|
+
# Returns an array of StructMemberInfo objects describing the struct's
|
259
|
+
# members.
|
260
|
+
#
|
261
|
+
def members
|
262
|
+
self::MEMBERS
|
263
|
+
end
|
264
|
+
|
265
|
+
#
|
266
|
+
# Returns the offset of a member.
|
267
|
+
#
|
268
|
+
def offset_of(member)
|
269
|
+
self::MEMBERS_HASH[member].offset
|
270
|
+
end
|
271
|
+
|
272
|
+
|
273
|
+
#
|
274
|
+
# Returns the type name of a member.
|
275
|
+
#
|
276
|
+
def type_of(member)
|
277
|
+
self::MEMBERS_HASH[member].type
|
278
|
+
end
|
279
|
+
|
280
|
+
|
281
|
+
#
|
282
|
+
# Returns the size in bytes of a member.
|
283
|
+
#
|
284
|
+
def bytesize_of(member)
|
285
|
+
self::MEMBERS_HASH[member].size
|
286
|
+
end
|
287
|
+
|
288
|
+
|
289
|
+
#
|
290
|
+
# Returns the alignment of a member.
|
291
|
+
#
|
292
|
+
def alignment_of(member)
|
293
|
+
self::MEMBERS_HASH[member].alignment
|
294
|
+
end
|
295
|
+
|
296
|
+
|
297
|
+
#
|
298
|
+
# Returns the length of a member.
|
299
|
+
#
|
300
|
+
def length_of(member)
|
301
|
+
self::MEMBERS_HASH[member].length
|
302
|
+
end
|
303
|
+
|
304
|
+
end # module MemberInfoSupport
|