cstruct 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/README.md +81 -0
  2. data/cstruct.gemspec +10 -0
  3. data/doc/examples/anonymous_struct.rb.html +94 -94
  4. data/doc/examples/anonymous_union.rb.html +93 -93
  5. data/doc/examples/array_member.rb.html +79 -79
  6. data/doc/examples/file_io.rb.html +87 -87
  7. data/doc/examples/first_example.rb.html +104 -104
  8. data/doc/examples/get_system_info.rb.html +114 -114
  9. data/doc/examples/get_versionex.rb.html +97 -97
  10. data/doc/examples/global_memory.rb.html +102 -102
  11. data/doc/examples/inner_struct.rb.html +79 -79
  12. data/doc/examples/inner_union.rb.html +77 -77
  13. data/doc/examples/namespace.rb.html +80 -80
  14. data/doc/examples/show_processes.rb.html +95 -95
  15. data/doc/examples/struct_member.rb.html +128 -128
  16. data/doc/stylesheets/ie.css +8 -8
  17. data/examples/anonymous_struct.rb +6 -6
  18. data/examples/anonymous_union.rb +5 -5
  19. data/examples/array_member.rb +1 -1
  20. data/examples/file_io.rb +22 -23
  21. data/examples/first_example.rb +4 -4
  22. data/examples/inner_struct.rb +5 -5
  23. data/examples/inner_union.rb +4 -4
  24. data/examples/namespace.rb +15 -15
  25. data/examples/win32/get_system_info.rb +31 -31
  26. data/examples/win32/get_versionex.rb +12 -12
  27. data/examples/win32/global_memory.rb +9 -9
  28. data/examples/win32/show_processes.rb +28 -28
  29. data/lib/cstruct.rb +1 -523
  30. data/lib/cstruct/cstruct.rb +457 -0
  31. data/lib/cstruct/field.rb +45 -0
  32. data/lib/cstruct/utils.rb +53 -0
  33. data/lib/cstruct/win32struct.rb +84 -0
  34. data/lib/cstruct/win64struct.rb +5 -0
  35. data/lib/win32struct.rb +3 -90
  36. data/lib/win64struct.rb +3 -13
  37. data/spec/cstruct_spec.rb +240 -0
  38. 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
+