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