rstruct 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -9,7 +9,7 @@ once, and use them in many ways as you see fit. Kind-of like... C structs in
9
9
  system/API header files.
10
10
 
11
11
  The structure declaration syntax emulates C structures syntax, although Rstruct
12
- is not intended to be able to parse C structures from C header files. (howewver
12
+ is not intended to be able to parse C structures from C header files. (however
13
13
  an addon could conceivably be built to do so with minimal fuss)
14
14
 
15
15
  Multiple approaches to parsing and building are supported and more can be added
@@ -21,10 +21,10 @@ as well as buffers.
21
21
  Rstruct was written because the authors wanted something comparable to C
22
22
  structures without having a strong need for extra 'magic' in parsing or
23
23
  building structures. While there exist numerous options in this space, they
24
- all seem to have suffered from a combination of performance and interface issues
25
- which limit their potential (and in some cases, spotty maintenance). Having,
26
- tried pretty much all of these alternatives (and even contributed to a few)
27
- in previous projects, the author still decided to write rstruct.
24
+ all seem to have suffered from a combination of performance and interface
25
+ issues which limit their potential (and in some cases, spotty maintenance).
26
+ Having, tried pretty much all of these alternatives (and even contributed to a
27
+ few) in previous projects, the author still decided to write rstruct.
28
28
 
29
29
  That said, the author does not claim Rstruct to be superior to any others, it's
30
30
  just another approach. There are several other excellent binary structure
@@ -58,7 +58,8 @@ Here is a trivial example defining and packing a raw structure
58
58
  # => "\x00\x00\x00A\x00\x00\x00B\x00\x00\x00C" # on a big endian machine
59
59
 
60
60
 
61
- Here is a fully functional Rstruct parser example using Apple's FAT file structure.
61
+ Here is a fully functional Rstruct parser example using Apple's FAT file
62
+ structure.
62
63
 
63
64
  # To compare the structs to their C counterparts, see:
64
65
  # http://fxr.watson.org/fxr/source/EXTERNAL_HEADERS/mach-o/fat.h?v=xnu-1228
@@ -100,7 +101,7 @@ Here is a fully functional Rstruct parser example using Apple's FAT file structu
100
101
 
101
102
  fat_arch = get_type(:fat_arch)
102
103
  (head.nfat_arch).times do |i|
103
- arch = fat_arch.read(f) # note, it reads to a seperate object on each arch
104
+ arch = fat_arch.read(f) # note, it reads to a new object on each arch
104
105
  puts " Architecture #{i}:"
105
106
  puts " " << dump(arch).join("\n ")
106
107
  puts
@@ -128,7 +129,8 @@ Here is a fully functional Rstruct parser example using Apple's FAT file structu
128
129
  align = 0x0000000c
129
130
 
130
131
 
131
- Please refer to rdoc, the samples/ directory, and unit tests for more information.
132
+ Please refer to rdoc, the samples/ directory, and unit tests for more
133
+ information.
132
134
 
133
135
  == Copyright
134
136
 
data/Rakefile CHANGED
@@ -45,6 +45,6 @@ Rake::RDocTask.new do |rdoc|
45
45
 
46
46
  rdoc.rdoc_dir = 'rdoc'
47
47
  rdoc.title = "rstruct #{version}"
48
- rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('README*', 'TODO*')
49
49
  rdoc.rdoc_files.include('lib/**/*.rb')
50
50
  end
data/TODO.rdoc ADDED
@@ -0,0 +1,10 @@
1
+ == TODO
2
+
3
+ * DOCUMENTATION!
4
+ * add a type for unions
5
+ * really no good reason why we can't support variable length data
6
+ * add types for odd byte-alignment values like 24-bit, 12-bit, 48-bit, etc.
7
+ * add a type for handling bitfields
8
+ * add instance helpers for resolving enums forward and back
9
+
10
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
@@ -0,0 +1,42 @@
1
+ require 'rstruct/registry'
2
+ require 'rstruct/field'
3
+ require 'rstruct/base_types'
4
+
5
+ module Rstruct
6
+ class ArrayContainer < Array
7
+ include ContainerMixins
8
+ end
9
+
10
+ class ArrayType < ContainerType
11
+ attr_reader :count
12
+
13
+ def initialize(name, typ, count, opts={}, &block)
14
+ opts = opts.dup
15
+ lkupreg = (opts.delete(:fields_from) || opts.delete(:register) || Rstruct.default_registry)
16
+ super(name, opts.merge(:register => false), &block)
17
+ @count = count
18
+ unless @rtype = lkupreg[typ]
19
+ raise(InvalidTypeError, "invalid array type #{typ.inspect}")
20
+ end
21
+ end
22
+
23
+ def typ
24
+ self
25
+ end
26
+
27
+ def fields
28
+ @fields ||= Array.new(self.count){|i| Field.new(i, @rtype, nil, nil) }.freeze
29
+ end
30
+
31
+ def instance(*values)
32
+ vals = values.flatten
33
+ ary = ArrayContainer.new(self.count){|f| vals.shift }
34
+ ary.rstruct_type = self
35
+ return ary
36
+ end
37
+
38
+ def offset_of(idx)
39
+ rtype.sizeof * idx
40
+ end
41
+ end
42
+ end
@@ -30,19 +30,19 @@ module Rstruct
30
30
  vals = (pvals.respond_to?(:values) ? pvals.values : pvals)
31
31
  vals ||= self.values
32
32
 
33
- opos = dst.pos
33
+ opos = dst.pos if dst.respond_to?(:pos)
34
34
  typ.fields.each_with_index do |f, i|
35
35
  fldval = vals[i]
36
36
  if fldval.respond_to?(:write)
37
37
  fldval.write(dst, fldval)
38
38
  else
39
- dst.write(f.typ.pack_value(fldval, self))
39
+ dst.write(f.typ.pack_value((fldval || 0), self))
40
40
  end
41
41
  end
42
42
  if dst.is_a?(StringIO) and pvals.nil?
43
43
  dst.pos = opos
44
44
  return(dst.read)
45
- else
45
+ elsif opos and dst.respond_to?(:pos)
46
46
  return dst.pos - opos
47
47
  end
48
48
  end
@@ -81,22 +81,36 @@ module Rstruct
81
81
  end
82
82
 
83
83
  def field_names
84
- @field_names ||= self.fields.map{|f| f.name }
84
+ self.fields.map{|f| f.name }
85
85
  end
86
86
 
87
87
  def field_types
88
- @field_types ||= self.fields.map{|f| f.typ }
88
+ self.fields.map{|f| f.typ }
89
89
  end
90
90
 
91
91
  def read(raw, obj=nil)
92
92
  raw = StringIO.new(raw) if raw.is_a?(String)
93
93
  obj = self.instance()
94
94
  fields.each do |f|
95
- obj[f.name] = f.read(raw, obj)
95
+ obj[f.name] = f.typ.read(raw, obj)
96
96
  end
97
97
  return obj
98
98
  end
99
99
 
100
+ def claim_value(vals, obj=nil)
101
+ if @claim_cb
102
+ @claim_cb.call(vals, obj)
103
+ else
104
+ # create our struct container
105
+ s = instance()
106
+
107
+ # iterate through the fields assigning values in the
108
+ # container and pass it along with values to each
109
+ # field's claim_value method.
110
+ self.fields.do {|f| s[f.name] = f.typ.claim_value(vals,s) }
111
+ return s
112
+ end
113
+ end
100
114
  end
101
115
 
102
116
  end
@@ -61,10 +61,10 @@ module Rstruct
61
61
 
62
62
  class PackedType < Type
63
63
  include Packable
64
- attr_reader :size, :format
64
+ attr_reader :sizeof, :format
65
65
 
66
66
  def initialize(name, size, format, opts={}, &block)
67
- @size = size
67
+ @sizeof = size
68
68
  @format = format
69
69
  @groupable = true
70
70
  super(name, opts, &block)
@@ -39,7 +39,7 @@ module Rstruct
39
39
  end
40
40
 
41
41
  def sizeof
42
- self.size
42
+ raise(NotImplementedError, "sizeof not implemented in #{self.class}")
43
43
  end
44
44
 
45
45
  private
@@ -3,3 +3,4 @@ require 'rstruct/registry'
3
3
  require 'rstruct/base_types/type.rb'
4
4
  require 'rstruct/base_types/packed_type.rb'
5
5
  require 'rstruct/base_types/container_type.rb'
6
+ require 'rstruct/base_types/array_type.rb'
data/lib/rstruct/field.rb CHANGED
@@ -1,11 +1,10 @@
1
1
  module Rstruct
2
2
  class Field
3
- attr_reader :name, :typ_name, :typ, :args, :block
3
+ attr_reader :name, :typ, :args, :block
4
4
 
5
- def initialize(name, typ, typ_name, args, block)
5
+ def initialize(name, typ, args, block)
6
6
  @name=name
7
7
  @typ=typ
8
- @typ_name = typ_name || typ
9
8
  @args=args
10
9
  @block=block
11
10
  end
@@ -1,5 +1,6 @@
1
1
  require 'rstruct/registry'
2
2
  require 'rstruct/field'
3
+ require 'rstruct/base_types/array_type'
3
4
 
4
5
  module Rstruct
5
6
 
@@ -13,7 +14,11 @@ module Rstruct
13
14
  end
14
15
 
15
16
  def field(name, typ, typ_name, *args, &block)
16
- @__fields << Field.new(name,typ,typ_name,args,block)
17
+ @__fields << Field.new(name,typ,args,block)
18
+ end
19
+
20
+ def array(name, typ, count, opts={}, &block)
21
+ @__fields << ArrayType.new(name, typ, count, opts, &block)
17
22
  end
18
23
 
19
24
  def method_missing(typ_arg, *args, &block)
@@ -40,20 +40,13 @@ module Rstruct
40
40
  return s
41
41
  end
42
42
 
43
- def claim_value(vals, obj=nil)
44
- if @claim_cb
45
- @claim_cb.call(vals, obj)
46
- else
47
- # create our struct container
48
- s = instance()
49
-
50
- # iterate through the fields assigning values in the
51
- # container and pass it along with values to each
52
- # field's claim_value method.
53
- self.fields.do {|f| s[f.name] = f.typ.claim_value(vals,s) }
54
-
55
- return s
43
+ def offset_of(fld)
44
+ o = 0
45
+ self.fields.each do |f|
46
+ return o if f.name == fld
47
+ o += f.sizeof
56
48
  end
49
+ raise(InvalidTypeError, "Invalid type: #{fld}")
57
50
  end
58
51
  end
59
52
  end
data/lib/rstruct/types.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'rstruct/base_types'
2
2
 
3
3
  module Rstruct
4
- Char = PackedType.new(:char, 1, "A", :alias => [:char, :char_t])
4
+ Char = PackedType.new(:char, 1, "a", :alias => [:char, :char_t])
5
5
 
6
6
  Byte = PackedType.new(:byte, 1, "c", :alias => [:BYTE, :signed_byte])
7
7
 
data/lib/rstruct.rb CHANGED
@@ -41,7 +41,7 @@ module Rstruct
41
41
  end
42
42
 
43
43
  def typedef(p, t, opts={})
44
- reg = opts[:registry] || default_registry
44
+ reg = opts[:register] || default_registry
45
45
  reg.typedef(p,t,opts)
46
46
  end
47
47
 
data/samples/pe.rb ADDED
@@ -0,0 +1,262 @@
1
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
2
+ require 'rstruct'
3
+
4
+ module Pe
5
+ extend Rstruct::ClassMethods
6
+
7
+ PE_REGISTRY = Rstruct::Registry.new(:portable_executable)
8
+
9
+ def self.registry
10
+ PE_REGISTRY
11
+ end
12
+
13
+ typedef :uint16le, :WORD, :register => registry()
14
+ typedef :uint32le, :LONG, :register => registry()
15
+ typedef :uint32le, :DWORD, :register => registry()
16
+
17
+ IMAGE_DOS_HEADER = struct(:IMAGE_DOS_HEADER, :register => registry()) {
18
+ WORD :magic # magic number
19
+ WORD :cblp # bytes on last page of file
20
+ WORD :cp # pages in file
21
+ WORD :crlc # relocations
22
+ WORD :cparhdr # size of header in paragraphs
23
+ WORD :minalloc # minimum extra paragraphs needed
24
+ WORD :maxalloc # maxumum extra paragraphs needed
25
+ WORD :ss # Initial (relative) SS value
26
+ WORD :sp # Initial SP value
27
+ WORD :csum # Checksum
28
+ WORD :ip # Initial IP value
29
+ WORD :cs # Initial (relative) CS value
30
+ WORD :lfarlc # File address of relocation table
31
+ WORD :ovno # overlay number
32
+ array :res1, :uint16le, 4
33
+ WORD :oemid # OEM identifier
34
+ WORD :oeminfo # OEM information; oem specific
35
+ array :res2, :uint16le, 10
36
+ LONG :lfanew # File address of new exe header (PE header)
37
+ }
38
+
39
+
40
+
41
+ IMAGE_FILE_MACHINE_TYPES = {
42
+ 0 => :UNKNOWN,
43
+ 0x014C => :I386,
44
+ 0x8664 => :AMD64,
45
+ 0x01C0 => :ARM,
46
+ 0x0162 => :R3000,
47
+ 0x0166 => :R4000,
48
+ 0x0168 => :R10000,
49
+ 0x0169 => :WCEMIPSV2,
50
+ 0x0184 => :ALPHA,
51
+ 0x01A2 => :SH3,
52
+ 0x01A3 => :SH3DSP,
53
+ 0x01A4 => :SH3E,
54
+ 0x01A6 => :SH4,
55
+ 0x01A8 => :SH5,
56
+ 0x01C2 => :THUMB,
57
+ 0x01D3 => :AM33,
58
+ 0x01F0 => :POWERPC,
59
+ 0x01F1 => :POWERPCFP,
60
+ 0x0200 => :IA64,
61
+ 0x0266 => :MIPS16,
62
+ 0x0284 => :ALPHA64,
63
+ 0x0366 => :MIPSFPU,
64
+ 0x0466 => :MIPSFPU16,
65
+ 0x0520 => :TRICORE,
66
+ 0x0CEF => :CEF,
67
+ 0x0EBC => :EBC,
68
+ 0x9041 => :M32R,
69
+ 0xC0EE => :CEE,
70
+ }
71
+
72
+ IMAGE_FILE_CHARACTERISTICS_FLAGS = {
73
+ :RELOCS_STRIPPED => 0x0001,
74
+ :EXECUTABLE_IMAGE => 0x0002,
75
+ :LINE_NUMS_STRIPPED => 0x0004,
76
+ :LOCAL_SYMS_STRIPPED => 0x0008,
77
+ :AGGRESIVE_WS_TRIM => 0x0010,
78
+ :LARGE_ADDRESS_AWARE => 0x0020,
79
+ :BYTES_REVERSED_LO => 0x0080,
80
+ :MACHINE_32BIT => 0x0100,
81
+ :DEBUG_STRIPPED => 0x0200,
82
+ :REMOVABLE_RUN_FROM_SWAP => 0x0400,
83
+ :NET_RUN_FROM_SWAP => 0x0800,
84
+ :SYSTEM => 0x1000,
85
+ :DLL => 0x2000,
86
+ :UNIPROC_SYSTEM_ONLY => 0x4000,
87
+ :BYTES_REVERSED_HI => 0x8000,
88
+ }
89
+
90
+ IMAGE_FILE_HEADER = struct(:IMAGE_FILE_HEADER, :register => registry()) {
91
+ WORD :Machine
92
+ WORD :NumberOfSections
93
+ DWORD :TimeDateStamp
94
+ DWORD :PointerToSymbolTable
95
+ DWORD :NumberOfSymbols
96
+ WORD :SizeOfOptionalHeader
97
+ WORD :Characteristics
98
+ }
99
+
100
+ IMAGE_DATA_DIRECTORY = struct(:IMAGE_DATA_DIRECTORY, :register => registry()) {
101
+ DWORD :VirtualAddress
102
+ DWORD :Size
103
+ }
104
+
105
+ IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
106
+
107
+ IMAGE_NT_OPTIONAL_HDR_MAGIC = {
108
+ 0x10b => :HDR32,
109
+ 0x20b => :HDR64,
110
+ 0x107 => :ROM,
111
+ }
112
+
113
+ IMAGE_NT_OPTIONAL_HDR_SUBSYSTEMS = [
114
+ :UNKNOWN,
115
+ :NATIVE,
116
+ :WINDOWS_GUI,
117
+ :WINDOWS_CONSOLE,
118
+ nil,
119
+ :OS2_CONSOLE,
120
+ nil,
121
+ :POSIX_CONSOLE,
122
+ :NATIVE_WIN9X_DRIVER,
123
+ :WINDOWS_CE_GUI,
124
+ :EFI_APPLICATION,
125
+ :EFI_BOOT_SERVICE_DRIVER,
126
+ :EFI_RUNTIME_DRIVER,
127
+ :EFI_ROM,
128
+ :XBOX,
129
+ nil,
130
+ :WINDOWS_BOOT_APPLICATION,
131
+ ]
132
+
133
+ IMAGE_DLL_CHARACTERISTICS_FLAGS = {
134
+ :DYNAMIC_BASE => 0x0080,
135
+ :FORCE_INTEGRITY => 0x0080,
136
+ :NX_COMPAT => 0x0100,
137
+ :NO_ISOLATION => 0x0200,
138
+ :NO_SEH => 0x0400,
139
+ :NO_BIND => 0x0800,
140
+ :WDM_DRIVER => 0x2000,
141
+ :TERM_SVR_AWARE => 0x8000,
142
+ }
143
+
144
+ IMAGE_OPTIONAL_HEADER = struct(:IMAGE_OPTIONAL_HEADER, :register => registry()) {
145
+ WORD :Magic
146
+ BYTE :MajorLinkerVersion
147
+ BYTE :MinorLinkerVersion
148
+ DWORD :SizeOfCode
149
+ DWORD :SizeOfInitializedData
150
+ DWORD :SizeOfUninitializedData
151
+ DWORD :AddressOfEntryPoint
152
+ DWORD :BaseOfCode
153
+ DWORD :BaseOfData
154
+ DWORD :ImageBase
155
+ DWORD :SectionAlignment
156
+ DWORD :FileAlignment
157
+ WORD :MajorOperatingSystemVersion
158
+ WORD :MinorOperatingSystemVersion
159
+ WORD :MajorImageVersion
160
+ WORD :MinorImageVersion
161
+ WORD :MajorSubsystemVersion
162
+ WORD :MinorSubsystemVersion
163
+ DWORD :Win32VersionValue
164
+ DWORD :SizeOfImage
165
+ DWORD :SizeOfHeaders
166
+ DWORD :CheckSum
167
+ WORD :Subsystem
168
+ WORD :DllCharacteristics
169
+ DWORD :SizeOfStackReserve
170
+ DWORD :SizeOfStackCommit
171
+ DWORD :SizeOfHeapReserve
172
+ DWORD :SizeOfHeapCommit
173
+ DWORD :LoaderFlags
174
+ DWORD :NumberOfRvaAndSizes
175
+
176
+ IMAGE_DATA_DIRECTORY :DDExportTable
177
+ IMAGE_DATA_DIRECTORY :DDImportTable
178
+ IMAGE_DATA_DIRECTORY :DDResourceTable
179
+ IMAGE_DATA_DIRECTORY :DDExceptionTable
180
+ IMAGE_DATA_DIRECTORY :DDCertificatTable
181
+ IMAGE_DATA_DIRECTORY :DDBaseRelocationTable
182
+ IMAGE_DATA_DIRECTORY :DDDebuggingInformation
183
+ IMAGE_DATA_DIRECTORY :DDArchSpecificData
184
+ IMAGE_DATA_DIRECTORY :DDGlobalPointerRegisterRVA
185
+ IMAGE_DATA_DIRECTORY :DDTlsTable
186
+ IMAGE_DATA_DIRECTORY :DDLoadConfigurationTable
187
+ IMAGE_DATA_DIRECTORY :DDBoundImportTable
188
+ IMAGE_DATA_DIRECTORY :DDImportAddressTable
189
+ IMAGE_DATA_DIRECTORY :DDDelayImportDescriptor
190
+ IMAGE_DATA_DIRECTORY :DDClrHeader
191
+ IMAGE_DATA_DIRECTORY :DDReserved
192
+ }
193
+
194
+ IMAGE_NT_HEADERS = struct(:IMAGE_NT_HEADERS, :register => registry()) {
195
+ DWORD :Signature
196
+ IMAGE_FILE_HEADER :FileHeader
197
+ IMAGE_OPTIONAL_HEADER :OptionalHeader
198
+ }
199
+
200
+ IMAGE_SIZEOF_SHORT_NAME = 8
201
+
202
+ IMAGE_SECTION_HEADER = struct(:IMAGE_SECTION_HEADER, :register => registry()) {
203
+ array :Name, :char, 8
204
+ DWORD :PhysicalAddress
205
+ DWORD :VirtualAddress
206
+ DWORD :SizeOfRawData
207
+ DWORD :PointerToRawData
208
+ DWORD :PointerToRelocations
209
+ DWORD :PointerToLinenumbers
210
+ WORD :NumberOfRelocations
211
+ WORD :NumberOfLinenumbers
212
+ DWORD :Characteristics
213
+ }
214
+
215
+ def self.parse_header(*args)
216
+ Headers.new(*args)
217
+ end
218
+
219
+ class Headers
220
+ attr_reader :dos, :stub, :coff, :sections, :file_data
221
+
222
+ def initialize(input, param = {})
223
+ input = StringIO.new(input) if input.is_a?(String)
224
+ @dos = IMAGE_DOS_HEADER.read(input)
225
+ @stub = input.read(@dos.lfanew - IMAGE_DOS_HEADER.sizeof)
226
+ @coff = IMAGE_NT_HEADERS.read(input)
227
+ @sections = Array.new(@coff.FileHeader.NumberOfSections) do
228
+ IMAGE_SECTION_HEADER.read(input)
229
+ end
230
+ end
231
+
232
+ # Returns a Time object representation of the
233
+ # PE header timestamp.
234
+ def timestamp
235
+ Time.at(coff.FileHeader.TimeDateStamp)
236
+ end
237
+
238
+ # Returns the size of the PE file based on header
239
+ # information. Specifically, the last section's
240
+ # PointerToRawData is added to its SizeOfRawData
241
+ # which should be equal to the file size.
242
+ def file_size
243
+ lseg = self.sections.last
244
+ lseg.PointerToRawData + lseg.SizeOfRawData
245
+ end
246
+
247
+ def repack(io=nil)
248
+ io ||= StringIO.new
249
+ self.dos.write(io)
250
+ io.write(self.stub)
251
+ self.coff.write(io)
252
+ self.sections.each{ |s| s.write(io) }
253
+ io.string if io.is_a?(StringIO)
254
+ end
255
+
256
+ # the checksum value from the parsed header
257
+ def checksum
258
+ coff.OptionalHeader.CheckSum
259
+ end
260
+
261
+ end
262
+ end
@@ -0,0 +1,24 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'type_behaviors'
3
+
4
+ describe Rstruct::ArrayType do
5
+ context "a basic character array" do
6
+ before(:each) do
7
+ @struct = Rstruct.struct(:array_test, :register => false) {
8
+ array :test_array, :char, 8
9
+ }
10
+
11
+ @pack_format = "aaaaaaaa"
12
+
13
+ @instance = @struct.instance
14
+
15
+ @rawdata = "ABCDEFGH"
16
+ @populate = lambda { @rawdata.bytes.each_with_index{|b,i| @instance.test_array[i] = b.chr} }
17
+ @verify_unpack = lambda {|ret| @rawdata.bytes.each_with_index{|b,i| ret.test_array[i].should == b.chr } }
18
+ end
19
+
20
+ it_should_behave_like "a packable type"
21
+ it_should_behave_like "a groupable type"
22
+ end
23
+ end
24
+
@@ -1,6 +1,50 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
  require 'type_behaviors'
3
3
 
4
+ # Applies to structures
5
+ shared_examples_for "a structure" do
6
+
7
+ it "should return a value instance with a reference back to itself" do
8
+ s = @struct.instance()
9
+ s.rstruct_type.should == @struct
10
+ end
11
+
12
+ context "struct instance" do
13
+ it "should expose the same fields as the struct they belong to" do
14
+ @struct.field_names.each do |name|
15
+ name = name.to_s if RUBY_VERSION < '1.9'
16
+ @instance.members.should include(name)
17
+ end
18
+ end
19
+
20
+ it "should allow struct field values to be set and retrieved with accessors" do
21
+ @values.each do |k,v|
22
+ @instance.__send__(k).should be_nil
23
+ @instance.__send__("#{k}=", v).should == v
24
+ @instance.__send__(k).should == v
25
+ end
26
+ end
27
+
28
+ it "should allow field values to be set with arguments to instance creation" do
29
+ s=@struct.instance(@values)
30
+ s.rstruct_type.should == @struct
31
+ @values.each { |k,v| s.__send__(k).should == v }
32
+ end
33
+
34
+ it "should yield itself to a block on instance creation" do
35
+ i=@struct.instance do |s|
36
+ @values.each do |k,v|
37
+ s.__send__(k).should be_nil
38
+ s.__send__("#{k}=", v).should == v
39
+ s.__send__(k).should == v
40
+ end
41
+ end
42
+
43
+ @values.each { |k,v| i.__send__(k).should == v }
44
+ end
45
+ end
46
+ end
47
+
4
48
  describe Rstruct::Structure do
5
49
  context "initialization" do
6
50
  it "requires a block" do
@@ -208,6 +252,16 @@ describe Rstruct::Structure do
208
252
  it_should_behave_like "a structure"
209
253
  it_should_behave_like "a packable type"
210
254
  it_should_behave_like "a groupable type"
255
+
256
+ it "should return the correct offset for fields" do
257
+ @struct.offset_of(:magic).should == 0
258
+ @struct.offset_of(:cputype).should == 4
259
+ @struct.offset_of(:cpusubtype).should == 8
260
+ @struct.offset_of(:filetype).should == 12
261
+ @struct.offset_of(:ncmds).should == 16
262
+ @struct.offset_of(:sizeofcmds).should == 20
263
+ @struct.offset_of(:flags).should == 24
264
+ end
211
265
  end
212
266
 
213
267
  context "a simple fixed-length nested struct" do
@@ -278,7 +332,7 @@ describe Rstruct::Structure do
278
332
  double_inner_values.each {|k,v| @instance.inner.double_inner[k] = v}
279
333
  end
280
334
 
281
- @pack_format = "NNccAA"
335
+ @pack_format = "NNccaa"
282
336
  @rawdata = "\xde\xad\xbe\xef\xfa\xce\xfe\xeb\x01\x02\x41\x42"
283
337
 
284
338
  @verify_unpack = lambda do |ret|
@@ -7,23 +7,23 @@ shared_examples_for "a packable type" do
7
7
  it "should write raw data and return a string when no output is specified" do
8
8
  @populate.call()
9
9
  ret = @instance.write()
10
- ret.should == @rawdata
10
+ ret.bytes.to_a.should == @rawdata.bytes.to_a
11
11
  end
12
12
 
13
13
  it "should write raw data to a string object correctly" do
14
14
  s = "test"
15
15
  @populate.call()
16
16
  ret = @instance.write(s)
17
- ret.should == @rawdata
18
- s.should == "test" << @rawdata
17
+ ret.bytes.to_a.should == @rawdata.bytes.to_a
18
+ s.bytes.to_a.should == "test#{@rawdata}".bytes.to_a
19
19
  end
20
20
 
21
21
  it "should write raw data to a StringIO object correctly" do
22
22
  sio = StringIO.new
23
23
  @populate.call()
24
24
  ret = @instance.write(sio)
25
- ret.should == @rawdata
26
- sio.string.should == @rawdata
25
+ ret.bytes.to_a.should == @rawdata.bytes.to_a
26
+ sio.string.bytes.to_a.should == @rawdata.bytes.to_a
27
27
  end
28
28
 
29
29
  it "should write raw data to a File IO object correctly" do
@@ -34,7 +34,7 @@ shared_examples_for "a packable type" do
34
34
  ret = @instance.write(tempf)
35
35
  ret.should == @rawdata.size
36
36
  tempf.rewind
37
- tempf.read.should == "test" << @rawdata
37
+ tempf.read.bytes.to_a.should == "test#{@rawdata}".bytes.to_a
38
38
  ensure
39
39
  tempf.close if tempf
40
40
  end
@@ -81,7 +81,7 @@ shared_examples_for "a packable type" do
81
81
  else
82
82
  @values.each {|k,v| ret.__send__(k).should == v }
83
83
  end
84
- fio.read().should == testend
84
+ fio.read().bytes.to_a.should == testend.bytes.to_a
85
85
  ensure
86
86
  fio.close if fio
87
87
  end
@@ -96,7 +96,7 @@ shared_examples_for "a packable type" do
96
96
  @values.each {|k,v| ret.__send__(k).should == v }
97
97
  end
98
98
  repacked = ret.write()
99
- repacked.should == @rawdata
99
+ repacked.bytes.to_a.should == @rawdata.bytes.to_a
100
100
  end
101
101
 
102
102
  end
@@ -115,44 +115,4 @@ shared_examples_for "a groupable type" do
115
115
  end
116
116
  end
117
117
 
118
- # Applies to structures
119
- shared_examples_for "a structure" do
120
-
121
- it "should return a value instance with a reference back to itself" do
122
- s = @struct.instance()
123
- s.rstruct_type.should == @struct
124
- end
125
-
126
- context "struct instance" do
127
- it "should expose the same fields as the struct they belong to" do
128
- @struct.field_names.each {|name| @instance.members.should include(name.to_s) }
129
- end
130
-
131
- it "should allow struct field values to be set and retrieved with accessors" do
132
- @values.each do |k,v|
133
- @instance.__send__(k).should be_nil
134
- @instance.__send__("#{k}=", v).should == v
135
- @instance.__send__(k).should == v
136
- end
137
- end
138
-
139
- it "should allow field values to be set with arguments to instance creation" do
140
- s=@struct.instance(@values)
141
- s.rstruct_type.should == @struct
142
- @values.each { |k,v| s.__send__(k).should == v }
143
- end
144
-
145
- it "should yield itself to a block on instance creation" do
146
- i=@struct.instance do |s|
147
- @values.each do |k,v|
148
- s.__send__(k).should be_nil
149
- s.__send__("#{k}=", v).should == v
150
- s.__send__(k).should == v
151
- end
152
- end
153
-
154
- @values.each { |k,v| i.__send__(k).should == v }
155
- end
156
- end
157
- end
158
118
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 0
9
- version: 0.1.0
8
+ - 1
9
+ version: 0.1.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Eric Monti
@@ -14,10 +14,12 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-04-30 00:00:00 -05:00
17
+ date: 2011-08-25 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
21
23
  requirement: &id001 !ruby/object:Gem::Requirement
22
24
  requirements:
23
25
  - - ~>
@@ -27,11 +29,11 @@ dependencies:
27
29
  - 3
28
30
  - 0
29
31
  version: 2.3.0
30
- prerelease: false
31
32
  type: :development
32
- name: rspec
33
33
  version_requirements: *id001
34
34
  - !ruby/object:Gem::Dependency
35
+ name: bundler
36
+ prerelease: false
35
37
  requirement: &id002 !ruby/object:Gem::Requirement
36
38
  requirements:
37
39
  - - ~>
@@ -41,11 +43,11 @@ dependencies:
41
43
  - 0
42
44
  - 0
43
45
  version: 1.0.0
44
- prerelease: false
45
46
  type: :development
46
- name: bundler
47
47
  version_requirements: *id002
48
48
  - !ruby/object:Gem::Dependency
49
+ name: jeweler
50
+ prerelease: false
49
51
  requirement: &id003 !ruby/object:Gem::Requirement
50
52
  requirements:
51
53
  - - ~>
@@ -55,11 +57,11 @@ dependencies:
55
57
  - 5
56
58
  - 2
57
59
  version: 1.5.2
58
- prerelease: false
59
60
  type: :development
60
- name: jeweler
61
61
  version_requirements: *id003
62
62
  - !ruby/object:Gem::Dependency
63
+ name: rcov
64
+ prerelease: false
63
65
  requirement: &id004 !ruby/object:Gem::Requirement
64
66
  requirements:
65
67
  - - ">="
@@ -67,9 +69,7 @@ dependencies:
67
69
  segments:
68
70
  - 0
69
71
  version: "0"
70
- prerelease: false
71
72
  type: :development
72
- name: rcov
73
73
  version_requirements: *id004
74
74
  description: A library for working with Ruby binary structures in a way similar to c-structs
75
75
  email: esmonti@gmail.com
@@ -87,9 +87,11 @@ files:
87
87
  - LICENSE.txt
88
88
  - README.rdoc
89
89
  - Rakefile
90
+ - TODO.rdoc
90
91
  - VERSION
91
92
  - lib/rstruct.rb
92
93
  - lib/rstruct/base_types.rb
94
+ - lib/rstruct/base_types/array_type.rb
93
95
  - lib/rstruct/base_types/container_type.rb
94
96
  - lib/rstruct/base_types/packed_type.rb
95
97
  - lib/rstruct/base_types/type.rb
@@ -99,6 +101,8 @@ files:
99
101
  - lib/rstruct/structure.rb
100
102
  - lib/rstruct/types.rb
101
103
  - samples/fatparse.rb
104
+ - samples/pe.rb
105
+ - spec/array_spec.rb
102
106
  - spec/registry_behaviors.rb
103
107
  - spec/registry_spec.rb
104
108
  - spec/rstruct_spec.rb
@@ -136,6 +140,7 @@ signing_key:
136
140
  specification_version: 3
137
141
  summary: A library for working with Ruby binary structures in a way similar to c-structs
138
142
  test_files:
143
+ - spec/array_spec.rb
139
144
  - spec/registry_behaviors.rb
140
145
  - spec/registry_spec.rb
141
146
  - spec/rstruct_spec.rb