cstruct 1.0.0 → 1.0.1
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.
- data/README.md +81 -0
- data/cstruct.gemspec +10 -0
- data/doc/examples/anonymous_struct.rb.html +94 -94
- data/doc/examples/anonymous_union.rb.html +93 -93
- data/doc/examples/array_member.rb.html +79 -79
- data/doc/examples/file_io.rb.html +87 -87
- data/doc/examples/first_example.rb.html +104 -104
- data/doc/examples/get_system_info.rb.html +114 -114
- data/doc/examples/get_versionex.rb.html +97 -97
- data/doc/examples/global_memory.rb.html +102 -102
- data/doc/examples/inner_struct.rb.html +79 -79
- data/doc/examples/inner_union.rb.html +77 -77
- data/doc/examples/namespace.rb.html +80 -80
- data/doc/examples/show_processes.rb.html +95 -95
- data/doc/examples/struct_member.rb.html +128 -128
- data/doc/stylesheets/ie.css +8 -8
- data/examples/anonymous_struct.rb +6 -6
- data/examples/anonymous_union.rb +5 -5
- data/examples/array_member.rb +1 -1
- data/examples/file_io.rb +22 -23
- data/examples/first_example.rb +4 -4
- data/examples/inner_struct.rb +5 -5
- data/examples/inner_union.rb +4 -4
- data/examples/namespace.rb +15 -15
- data/examples/win32/get_system_info.rb +31 -31
- data/examples/win32/get_versionex.rb +12 -12
- data/examples/win32/global_memory.rb +9 -9
- data/examples/win32/show_processes.rb +28 -28
- data/lib/cstruct.rb +1 -523
- data/lib/cstruct/cstruct.rb +457 -0
- data/lib/cstruct/field.rb +45 -0
- data/lib/cstruct/utils.rb +53 -0
- data/lib/cstruct/win32struct.rb +84 -0
- data/lib/cstruct/win64struct.rb +5 -0
- data/lib/win32struct.rb +3 -90
- data/lib/win64struct.rb +3 -13
- data/spec/cstruct_spec.rb +240 -0
- metadata +62 -73
@@ -0,0 +1,457 @@
|
|
1
|
+
require 'cstruct/field'
|
2
|
+
require 'cstruct/utils'
|
3
|
+
#
|
4
|
+
# ===Description
|
5
|
+
# CStruct is a simulation of the C language's struct.
|
6
|
+
# Its main purpose is to manipulate binary-data conveniently in Ruby.
|
7
|
+
# ===Supported primitive types in CStruct
|
8
|
+
# * signed types:
|
9
|
+
# char,int8,int16,int32,int64,float,double
|
10
|
+
# * unsigned types:
|
11
|
+
# uchar,uint8,uint16,uint32,uint64
|
12
|
+
# ===More infomantion
|
13
|
+
# * source: http://github.com/skandhas/cstruct
|
14
|
+
# * document: http://cstruct.rubyforge.org/documents.html
|
15
|
+
# * examples: http://cstruct.rubyforge.org/examples.html
|
16
|
+
class CStruct
|
17
|
+
# version
|
18
|
+
VERSION = "1.0.1"
|
19
|
+
class << self
|
20
|
+
# set the options of a struct.
|
21
|
+
def options opts
|
22
|
+
@options.merge! opts
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return the endian of a struct;the default is :little.
|
26
|
+
def endian
|
27
|
+
@options[:endian]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return the align of a struct;the default is 1.
|
31
|
+
def align
|
32
|
+
@options[:align]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Return the fields of a struct.
|
36
|
+
def fields
|
37
|
+
@fields
|
38
|
+
end
|
39
|
+
# Return the size of a struct;+size+ similar to +sizeof+ in C language.
|
40
|
+
#
|
41
|
+
# Example:
|
42
|
+
#
|
43
|
+
# class Point < CStruct
|
44
|
+
# int32:x
|
45
|
+
# int32:y
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# puts Point.size # or Point.__size__
|
49
|
+
#
|
50
|
+
def size
|
51
|
+
@options[:layout_size]
|
52
|
+
end
|
53
|
+
|
54
|
+
alias_method :__size__, :size
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def self.init_class_variables
|
59
|
+
@fields = {}
|
60
|
+
@options= { :layout_size =>0, :endian => :little, :align => 1}
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.field(symbol,fsize,fsign,dimension = nil)
|
64
|
+
field = Field.new(symbol,fsize,@options[:layout_size],fsign,dimension)
|
65
|
+
@options[:layout_size] += field.byte_size
|
66
|
+
@fields[symbol] = field
|
67
|
+
dimension.is_a?(Array) ? (do_arrayfield field):(do_field field)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.structfield(symbol,sclass,ssize,dimension = nil)
|
71
|
+
field = Field.new(symbol,ssize,@options[:layout_size],:struct,dimension)
|
72
|
+
@options[:layout_size] += field.byte_size
|
73
|
+
@fields[symbol] = field
|
74
|
+
dimension.is_a?(Array) ? (do_arraystructfield field,sclass):(do_structfield field,sclass)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.inherited(clazz)
|
78
|
+
clazz.init_class_variables
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.method_missing(method, *args)
|
82
|
+
sclass = method_to_class_by_class self,method
|
83
|
+
sclass = method_to_class_by_class Object,method unless sclass
|
84
|
+
sclass = method_to_class_by_namespace method unless sclass
|
85
|
+
sclass = method_to_class method unless sclass
|
86
|
+
super unless sclass
|
87
|
+
is_cstruct = sclass.ancestors.select{|x| x == CStruct }
|
88
|
+
if is_cstruct
|
89
|
+
symbol,dimension = *args
|
90
|
+
structfield symbol,sclass,sclass.size,dimension
|
91
|
+
else
|
92
|
+
super
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.method_to_class_by_class(clazz,method)
|
97
|
+
method_string_or_symbol = RUBY_VERSION < '1.9' ? (method.to_s):(method)
|
98
|
+
unless clazz.constants.include? method_string_or_symbol
|
99
|
+
return nil
|
100
|
+
end
|
101
|
+
clazz.const_get method
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.method_to_class_by_namespace(method) #:nodoc: X::Y::A
|
105
|
+
sclass = nil
|
106
|
+
class_name_array = self.to_s.split('::')
|
107
|
+
return nil if class_name_array.size < 2
|
108
|
+
class_array = []
|
109
|
+
class_name_array.pop
|
110
|
+
class_name_array.inject(self){|m,class_name| class_array << m.const_get(class_name.to_sym); class_array.last }
|
111
|
+
class_array.reverse_each do |clazz|
|
112
|
+
if clazz.const_defined?(method)
|
113
|
+
sclass = clazz.const_get(method)
|
114
|
+
break
|
115
|
+
end
|
116
|
+
end
|
117
|
+
sclass
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.method_to_class(method) #:nodoc: X_Y_A
|
121
|
+
|
122
|
+
super_namespace = nil
|
123
|
+
class_name_array = self.to_s.split('::')
|
124
|
+
|
125
|
+
if class_name_array.size < 2
|
126
|
+
super_namespace = Object
|
127
|
+
else
|
128
|
+
class_name_array.pop
|
129
|
+
super_namespace = class_name_array.inject(Object) {|m,class_name| m.const_get(class_name.to_sym) }
|
130
|
+
end
|
131
|
+
|
132
|
+
namespace_name_array = method.to_s.split('_')
|
133
|
+
return nil if namespace_name_array.size < 2
|
134
|
+
sclass = namespace_name_array.inject(super_namespace) {|m,class_name| m.const_get(class_name.to_sym) }
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.do_field(field)
|
138
|
+
symbol = field.tag
|
139
|
+
define_method(symbol) { normal_field_to_value(symbol) }
|
140
|
+
define_method("#{symbol}=") { |value| value_to_field(symbol,value) }
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.do_arrayfield(field)
|
144
|
+
symbol = field.tag
|
145
|
+
define_method(symbol) { array_field_to_value(symbol) }
|
146
|
+
define_method("#{symbol}=") { |value| value_to_arrayfield(symbol,value) }
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.do_structfield(field,sclass)
|
150
|
+
symbol = field.tag
|
151
|
+
define_method(symbol) { struct_field_to_value(symbol,sclass)}
|
152
|
+
define_method("#{symbol}=") { |value| value_to_struct_field(symbol,sclass,value)}
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.do_arraystructfield(field,sclass)
|
156
|
+
symbol = field.tag
|
157
|
+
define_method(symbol) { array_struct_field_to_value(symbol,sclass) }
|
158
|
+
define_method("#{symbol}=") { |value| value_to_array_struct_field(symbol,sclass,value) }
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.unsigned(symbol,fsize,dimension)
|
162
|
+
field symbol,fsize,:unsigned,dimension
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.signed(symbol,fsize,dimension)
|
166
|
+
field symbol,fsize,:signed,dimension
|
167
|
+
end
|
168
|
+
|
169
|
+
public
|
170
|
+
class << self
|
171
|
+
# args =[symbol,dimension=nil]
|
172
|
+
[1,2,4,8].each do |i|
|
173
|
+
define_method("unsigned_#{i}byte") { |*args| unsigned(args[0],i,args[1]) }
|
174
|
+
define_method("signed_#{i}byte") { |*args| signed(args[0],i,args[1]) }
|
175
|
+
end
|
176
|
+
|
177
|
+
def float(*args) #:nodoc:
|
178
|
+
field args[0],4,:float,args[1]
|
179
|
+
end
|
180
|
+
|
181
|
+
def double(*args) #:nodoc:
|
182
|
+
field args[0],8,:double,args[1]
|
183
|
+
end
|
184
|
+
|
185
|
+
alias uint64 unsigned_8byte
|
186
|
+
alias uint32 unsigned_4byte
|
187
|
+
alias uint16 unsigned_2byte
|
188
|
+
alias uint8 unsigned_1byte
|
189
|
+
alias uchar unsigned_1byte
|
190
|
+
|
191
|
+
alias int64 signed_8byte
|
192
|
+
alias int32 signed_4byte
|
193
|
+
alias int16 signed_2byte
|
194
|
+
alias int8 signed_1byte
|
195
|
+
alias char signed_1byte
|
196
|
+
end
|
197
|
+
|
198
|
+
# call init_class_variables
|
199
|
+
init_class_variables
|
200
|
+
|
201
|
+
public
|
202
|
+
attr_accessor:owner
|
203
|
+
|
204
|
+
def initialize #:nodoc:
|
205
|
+
@data = "\0"*self.class.size
|
206
|
+
@data.encode!("BINARY") if RUBY_VERSION >= '1.9'
|
207
|
+
@owner = []
|
208
|
+
yield self if block_given?
|
209
|
+
end
|
210
|
+
|
211
|
+
# Return the data buffer of a CStruct's instance.
|
212
|
+
def data
|
213
|
+
@data
|
214
|
+
end
|
215
|
+
|
216
|
+
# Fill the date buffer of a CStruct's instance with zero.
|
217
|
+
def reset
|
218
|
+
(0...self.class.size).each { |i| Utils.string_setbyte @data,i, 0 }
|
219
|
+
sync_to_owner
|
220
|
+
end
|
221
|
+
|
222
|
+
# Assign to CStruct's instance.
|
223
|
+
def data= bindata
|
224
|
+
raise 'Data Type Error!' unless bindata.is_a? String
|
225
|
+
self << bindata
|
226
|
+
end
|
227
|
+
|
228
|
+
# Assign to CStruct's instance.
|
229
|
+
def << bindata
|
230
|
+
count = @data.size < bindata.size ? @data.size : bindata.size
|
231
|
+
(0...count).each do |i|
|
232
|
+
Utils.string_setbyte @data,i,Utils.string_getbyte(bindata,i)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def sync_to_owner #:nodoc:
|
237
|
+
return if @owner.empty?
|
238
|
+
final_offset = @owner.inject(0) do |sum,owner_value|
|
239
|
+
_,foffset,_ = owner_value
|
240
|
+
sum+= foffset
|
241
|
+
end
|
242
|
+
onwerdata,_,_ = @owner.last
|
243
|
+
Utils.buffer_setbytes onwerdata,@data,final_offset
|
244
|
+
end
|
245
|
+
|
246
|
+
alias __data__ data
|
247
|
+
alias __reset__ reset
|
248
|
+
alias __data__= data=
|
249
|
+
|
250
|
+
private
|
251
|
+
def normal_field_to_value(symbol)
|
252
|
+
make_normal_field(self.class.fields[symbol],self.class.endian)
|
253
|
+
end
|
254
|
+
|
255
|
+
def array_field_to_value(symbol)
|
256
|
+
make_array_normal_field(self.class.fields[symbol],self.class.endian)
|
257
|
+
end
|
258
|
+
|
259
|
+
def struct_field_to_value(symbol,sclass)
|
260
|
+
make_struct_field(self.class.fields[symbol],self.class.endian,sclass)
|
261
|
+
end
|
262
|
+
|
263
|
+
def array_struct_field_to_value(symbol,sclass)
|
264
|
+
make_array_struct_field(self.class.fields[symbol],self.class.endian,sclass)
|
265
|
+
end
|
266
|
+
|
267
|
+
def value_to_field(symbol,value)
|
268
|
+
dataref = @data
|
269
|
+
onwerref = @owner
|
270
|
+
self.class.class_eval do
|
271
|
+
field = @fields[symbol]
|
272
|
+
bin_string = CStruct::Utils::pack [value],@options[:endian],field.byte_size,field.sign
|
273
|
+
CStruct::Utils.buffer_setbytes dataref,bin_string,field.offset
|
274
|
+
end
|
275
|
+
sync_to_owner
|
276
|
+
end
|
277
|
+
|
278
|
+
|
279
|
+
def value_to_arrayfield(symbol,value)
|
280
|
+
field = self.class.fields[symbol]
|
281
|
+
array_length = field.byte_size/field.size
|
282
|
+
|
283
|
+
if field.size == 1 and value.is_a? String
|
284
|
+
if value.length >= array_length
|
285
|
+
puts "WARNING: \"#{value}\".length(#{value.length}) >= #{symbol}.length(#{dimension})!!"
|
286
|
+
end
|
287
|
+
|
288
|
+
value = value[0...array_length-1]
|
289
|
+
CStruct::Utils.buffer_setbytes @data,value,field.offset
|
290
|
+
sync_to_owner
|
291
|
+
else
|
292
|
+
raise "No Implement!(CStruct,version:#{CStruct::VERSION})"
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def value_to_struct_field(symbol,sclass,value)
|
297
|
+
raise "No Implement!(CStruct,version:#{CStruct::VERSION})"
|
298
|
+
end
|
299
|
+
|
300
|
+
def value_to_array_struct_field(symbol,sclass,value)
|
301
|
+
raise "No Implement!(CStruct,version:#{CStruct::VERSION})"
|
302
|
+
end
|
303
|
+
|
304
|
+
def make_normal_field(field,sendian,sclass = nil)
|
305
|
+
Utils::unpack(@data[field.offset,field.size],sendian,field.size,field.sign)
|
306
|
+
end
|
307
|
+
|
308
|
+
def make_struct_field(field,sendian,sclass)
|
309
|
+
value = sclass.new
|
310
|
+
value << @data[field.offset,field.size]
|
311
|
+
value.owner << [@data,field.offset,field.size]
|
312
|
+
unless self.owner.empty?
|
313
|
+
value.owner += self.owner
|
314
|
+
end
|
315
|
+
value
|
316
|
+
end
|
317
|
+
|
318
|
+
def make_array_normal_field(field,sendian,sclass = nil)
|
319
|
+
dataref= @data
|
320
|
+
objref = self
|
321
|
+
value = buffer_to_values field,sendian
|
322
|
+
|
323
|
+
def value.metaclass
|
324
|
+
class<<self; self; end
|
325
|
+
end
|
326
|
+
|
327
|
+
value.metaclass.__send__ :define_method,:[]= do |i,v|
|
328
|
+
bin_string = Utils::pack [v],sendian,field.size,field.sign
|
329
|
+
Utils.buffer_setbytes dataref,bin_string,field.offset + i * field.size
|
330
|
+
objref.sync_to_owner
|
331
|
+
end
|
332
|
+
|
333
|
+
case field.size
|
334
|
+
when 1
|
335
|
+
value.metaclass.__send__(:define_method,:to_cstr){ value.pack("C#{value.index(0)}") }
|
336
|
+
when 2
|
337
|
+
if RUBY_VERSION > "1.9"
|
338
|
+
utf_endian = {:little=>"LE",:big=>"BE"}
|
339
|
+
value.metaclass.__send__ :define_method,:to_wstr do
|
340
|
+
value.pack("S#{value.index(0)}").force_encoding("UTF-16#{utf_endian[sendian]}")
|
341
|
+
end
|
342
|
+
end
|
343
|
+
when 4
|
344
|
+
value.metaclass.__send__ :define_method,:to_wstr do
|
345
|
+
value.pack("L#{value.index(0)}").force_encoding("UTF-32#{utf_endian[sendian]}")
|
346
|
+
end
|
347
|
+
end
|
348
|
+
value
|
349
|
+
end
|
350
|
+
|
351
|
+
def make_array_struct_field(field,sendian,sclass)
|
352
|
+
buffer_to_structs field,sendian,sclass
|
353
|
+
end
|
354
|
+
|
355
|
+
def buffer_to_single_value(fsize,foffset,iterator,sendian,fsign)
|
356
|
+
CStruct::Utils::unpack(@data[foffset + iterator * fsize,fsize],sendian,fsize,fsign)
|
357
|
+
end
|
358
|
+
|
359
|
+
def buffer_to_values(field,sendian)
|
360
|
+
(0...field.byte_size/field.size).map do |i|
|
361
|
+
buffer_to_single_value(field.size,field.offset,i,sendian,field.sign)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def buffer_to_single_struct(sclass,ssize,soffset,iterator,sendian,fsign)
|
366
|
+
value = sclass.new
|
367
|
+
value << @data[soffset + iterator * ssize,ssize]
|
368
|
+
|
369
|
+
value.owner << [@data,soffset + iterator * ssize,ssize]
|
370
|
+
|
371
|
+
unless self.owner.empty?
|
372
|
+
value.owner += self.owner
|
373
|
+
end
|
374
|
+
value
|
375
|
+
end
|
376
|
+
|
377
|
+
def buffer_to_structs(field,sendian,sclass)
|
378
|
+
(0...field.byte_size/field.size).map do |i|
|
379
|
+
buffer_to_single_struct(sclass,field.size,field.offset,i,sendian,field.sign)
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
end
|
384
|
+
|
385
|
+
# Define a anonymous union field.
|
386
|
+
#
|
387
|
+
# Example:
|
388
|
+
# in C:
|
389
|
+
# struct U
|
390
|
+
# {
|
391
|
+
# union{
|
392
|
+
# int x;
|
393
|
+
# int y;
|
394
|
+
# }value; /* values is anonymous union's instance */
|
395
|
+
# };
|
396
|
+
#
|
397
|
+
# use CStruct in Ruby:
|
398
|
+
# class U < CStruct
|
399
|
+
# union:value do
|
400
|
+
# int32:x
|
401
|
+
# int32:y
|
402
|
+
# end
|
403
|
+
# end
|
404
|
+
#
|
405
|
+
def CStruct.union symbol,&block
|
406
|
+
union_super = self.ancestors[1]
|
407
|
+
union_class = Class.new(union_super) do
|
408
|
+
def self.change_to_union #:nodoc:
|
409
|
+
@fields.each_key { |symbol| @fields[symbol].offset = 0 }
|
410
|
+
|
411
|
+
max_field_size = @fields.values.inject(0)do |max,field|
|
412
|
+
dimension = field.dimension
|
413
|
+
dimension_product = 1
|
414
|
+
if dimension.is_a? Array
|
415
|
+
dimension_product = dimension.inject(1){|m,d| m *= d }
|
416
|
+
end
|
417
|
+
field_size = field.size* dimension_product
|
418
|
+
max = (field_size> max ? field_size : max)
|
419
|
+
end
|
420
|
+
@options[:layout_size] = max_field_size
|
421
|
+
end
|
422
|
+
end
|
423
|
+
union_class.instance_eval(&block)
|
424
|
+
union_class.instance_eval{change_to_union}
|
425
|
+
structfield symbol,union_class,union_class.size
|
426
|
+
end
|
427
|
+
|
428
|
+
# Define a anonymous struct field.
|
429
|
+
#
|
430
|
+
# Example:
|
431
|
+
#
|
432
|
+
# in C:
|
433
|
+
# struct Window
|
434
|
+
# {
|
435
|
+
# int style;
|
436
|
+
# struct{
|
437
|
+
# int x;
|
438
|
+
# int y;
|
439
|
+
# }position;
|
440
|
+
# };
|
441
|
+
#
|
442
|
+
# use CStruct in Ruby:
|
443
|
+
# class Window < CStruct
|
444
|
+
# int32:style
|
445
|
+
# struct :position do
|
446
|
+
# int32:x
|
447
|
+
# int32:y
|
448
|
+
# end
|
449
|
+
# end
|
450
|
+
#
|
451
|
+
def CStruct.struct symbol,&block
|
452
|
+
struct_super = self.ancestors[1]
|
453
|
+
struct_class = Class.new(struct_super)
|
454
|
+
struct_class.instance_eval(&block)
|
455
|
+
structfield symbol,struct_class,struct_class.__size__
|
456
|
+
end
|
457
|
+
|