snow-data 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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