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