jbangert-bindata 1.5.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.
Files changed (71) hide show
  1. data/.gitignore +1 -0
  2. data/BSDL +22 -0
  3. data/COPYING +52 -0
  4. data/ChangeLog.rdoc +204 -0
  5. data/Gemfile +2 -0
  6. data/INSTALL +11 -0
  7. data/NEWS.rdoc +164 -0
  8. data/README.md +54 -0
  9. data/Rakefile +13 -0
  10. data/bindata.gemspec +31 -0
  11. data/doc/manual.haml +407 -0
  12. data/doc/manual.md +1649 -0
  13. data/examples/NBT.txt +149 -0
  14. data/examples/gzip.rb +161 -0
  15. data/examples/ip_address.rb +22 -0
  16. data/examples/list.rb +124 -0
  17. data/examples/nbt.rb +178 -0
  18. data/lib/bindata.rb +33 -0
  19. data/lib/bindata/alignment.rb +83 -0
  20. data/lib/bindata/array.rb +335 -0
  21. data/lib/bindata/base.rb +388 -0
  22. data/lib/bindata/base_primitive.rb +214 -0
  23. data/lib/bindata/bits.rb +87 -0
  24. data/lib/bindata/choice.rb +216 -0
  25. data/lib/bindata/count_bytes_remaining.rb +35 -0
  26. data/lib/bindata/deprecated.rb +50 -0
  27. data/lib/bindata/dsl.rb +312 -0
  28. data/lib/bindata/float.rb +80 -0
  29. data/lib/bindata/int.rb +184 -0
  30. data/lib/bindata/io.rb +274 -0
  31. data/lib/bindata/lazy.rb +105 -0
  32. data/lib/bindata/offset.rb +91 -0
  33. data/lib/bindata/params.rb +135 -0
  34. data/lib/bindata/primitive.rb +135 -0
  35. data/lib/bindata/record.rb +110 -0
  36. data/lib/bindata/registry.rb +92 -0
  37. data/lib/bindata/rest.rb +35 -0
  38. data/lib/bindata/sanitize.rb +290 -0
  39. data/lib/bindata/skip.rb +48 -0
  40. data/lib/bindata/string.rb +145 -0
  41. data/lib/bindata/stringz.rb +96 -0
  42. data/lib/bindata/struct.rb +388 -0
  43. data/lib/bindata/trace.rb +94 -0
  44. data/lib/bindata/version.rb +3 -0
  45. data/setup.rb +1585 -0
  46. data/spec/alignment_spec.rb +61 -0
  47. data/spec/array_spec.rb +331 -0
  48. data/spec/base_primitive_spec.rb +238 -0
  49. data/spec/base_spec.rb +376 -0
  50. data/spec/bits_spec.rb +163 -0
  51. data/spec/choice_spec.rb +263 -0
  52. data/spec/count_bytes_remaining_spec.rb +38 -0
  53. data/spec/deprecated_spec.rb +31 -0
  54. data/spec/example.rb +21 -0
  55. data/spec/float_spec.rb +37 -0
  56. data/spec/int_spec.rb +216 -0
  57. data/spec/io_spec.rb +352 -0
  58. data/spec/lazy_spec.rb +217 -0
  59. data/spec/primitive_spec.rb +202 -0
  60. data/spec/record_spec.rb +530 -0
  61. data/spec/registry_spec.rb +108 -0
  62. data/spec/rest_spec.rb +26 -0
  63. data/spec/skip_spec.rb +27 -0
  64. data/spec/spec_common.rb +58 -0
  65. data/spec/string_spec.rb +300 -0
  66. data/spec/stringz_spec.rb +118 -0
  67. data/spec/struct_spec.rb +350 -0
  68. data/spec/system_spec.rb +380 -0
  69. data/tasks/manual.rake +36 -0
  70. data/tasks/rspec.rake +17 -0
  71. metadata +208 -0
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
4
+ require 'bindata/bits'
5
+ require 'bindata/int'
6
+ require 'bindata/float'
7
+ require 'bindata/registry'
8
+
9
+ describe BinData::Registry do
10
+ A = Class.new
11
+ B = Class.new
12
+ C = Class.new
13
+ D = Class.new
14
+
15
+ let(:r) { BinData::Registry.new }
16
+
17
+ it "lookups registered names" do
18
+ r.register('ASubClass', A)
19
+ r.register('AnotherSubClass', B)
20
+
21
+ r.lookup('ASubClass').should == A
22
+ r.lookup('a_sub_class').should == A
23
+ r.lookup('AnotherSubClass').should == B
24
+ r.lookup('another_sub_class').should == B
25
+ end
26
+
27
+ it "does not lookup unregistered names" do
28
+ expect {
29
+ r.lookup('a_non_existent_sub_class')
30
+ }.to raise_error(BinData::UnRegisteredTypeError)
31
+ end
32
+
33
+ it "unregisters names" do
34
+ r.register('ASubClass', A)
35
+ r.unregister('ASubClass')
36
+
37
+ expect {
38
+ r.lookup('ASubClass')
39
+ }.to raise_error(BinData::UnRegisteredTypeError)
40
+ end
41
+
42
+ it "allows overriding of registered classes" do
43
+ r.register('A', A)
44
+ r.register('A', B)
45
+
46
+ r.lookup('a').should == B
47
+ end
48
+
49
+ it "converts CamelCase to underscores" do
50
+ r.underscore_name('CamelCase').should == 'camel_case'
51
+ end
52
+
53
+ it "converts adjacent caps camelCase to underscores" do
54
+ r.underscore_name('XYZCamelCase').should == 'xyz_camel_case'
55
+ end
56
+
57
+ it "ignores the outer nestings of classes" do
58
+ r.underscore_name('A::B::C').should == 'c'
59
+ end
60
+ end
61
+
62
+ describe BinData::Registry, "with numerics" do
63
+ let(:r) { BinData::RegisteredClasses }
64
+
65
+ it "lookup integers with endian" do
66
+ r.lookup("int24", :big).to_s.should == "BinData::Int24be"
67
+ r.lookup("int24", :little).to_s.should == "BinData::Int24le"
68
+ r.lookup("uint24", :big).to_s.should == "BinData::Uint24be"
69
+ r.lookup("uint24", :little).to_s.should == "BinData::Uint24le"
70
+ end
71
+
72
+ it "does not lookup integers without endian" do
73
+ expect {
74
+ r.lookup("int24")
75
+ }.to raise_error(BinData::UnRegisteredTypeError)
76
+ end
77
+
78
+ it "does not lookup non byte based integers" do
79
+ expect {
80
+ r.lookup("int3")
81
+ }.to raise_error(BinData::UnRegisteredTypeError)
82
+ expect {
83
+ r.lookup("int3", :big)
84
+ }.to raise_error(BinData::UnRegisteredTypeError)
85
+ expect {
86
+ r.lookup("int3", :little)
87
+ }.to raise_error(BinData::UnRegisteredTypeError)
88
+ end
89
+
90
+ it "lookup floats with endian" do
91
+ r.lookup("float", :big).to_s.should == "BinData::FloatBe"
92
+ r.lookup("float", :little).to_s.should == "BinData::FloatLe"
93
+ r.lookup("double", :big).to_s.should == "BinData::DoubleBe"
94
+ r.lookup("double", :little).to_s.should == "BinData::DoubleLe"
95
+ end
96
+
97
+ it "lookup bits" do
98
+ r.lookup("bit5").to_s.should == "BinData::Bit5"
99
+ r.lookup("bit6le").to_s.should == "BinData::Bit6le"
100
+ end
101
+
102
+ it "lookup bits by ignoring endian" do
103
+ r.lookup("bit2", :big).to_s.should == "BinData::Bit2"
104
+ r.lookup("bit3le", :big).to_s.should == "BinData::Bit3le"
105
+ r.lookup("bit2", :little).to_s.should == "BinData::Bit2"
106
+ r.lookup("bit3le", :little).to_s.should == "BinData::Bit3le"
107
+ end
108
+ end
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
4
+ require 'bindata/rest'
5
+
6
+ describe BinData::Rest do
7
+ it { should == "" }
8
+
9
+ it "reads till end of stream" do
10
+ data = "abcdefghij"
11
+ subject.read(data).should == data
12
+ end
13
+
14
+ it "allows setting value for completeness" do
15
+ subject.assign("123")
16
+ subject.should == "123"
17
+ subject.to_binary_s.should == "123"
18
+ end
19
+
20
+ it "accepts BinData::BasePrimitive parameters" do
21
+ rest = BinData::Rest.new(:check_value => "abc")
22
+ expect {
23
+ rest.read("xyz")
24
+ }.to raise_error(BinData::ValidityError)
25
+ end
26
+ end
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
4
+ require 'bindata/skip'
5
+
6
+ describe BinData::Skip do
7
+ subject { BinData::Skip.new(:length => 5) }
8
+ let(:io) { StringIO.new("abcdefghij") }
9
+
10
+ it { should == "" }
11
+ its(:to_binary_s) { should == "\000" * 5 }
12
+
13
+ it "skips bytes" do
14
+ subject.read(io)
15
+ io.pos.should == 5
16
+ end
17
+
18
+ it "has expected binary representation after setting value" do
19
+ subject.assign("123")
20
+ subject.to_binary_s.should == "\000" * 5
21
+ end
22
+
23
+ it "has expected binary representation after reading" do
24
+ subject.read(io)
25
+ subject.to_binary_s.should == "\000" * 5
26
+ end
27
+ end
@@ -0,0 +1,58 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
2
+
3
+ require 'rspec'
4
+ require 'rspec/autorun'
5
+ require 'stringio'
6
+
7
+ class Object
8
+ def expose_methods_for_testing
9
+ cls = (Class === self) ? self : (class << self ; self; end)
10
+ private_method_names = cls.private_instance_methods - Object.private_instance_methods
11
+ cls.send(:public, *private_method_names)
12
+
13
+ protected_method_names = cls.protected_instance_methods - Object.protected_instance_methods
14
+ cls.send(:public, *protected_method_names)
15
+ end
16
+
17
+ def value_read_from_written
18
+ self.class.read(self.to_binary_s)
19
+ end
20
+ end
21
+
22
+ class StringIO
23
+ # Returns the value that was written to the io
24
+ def value
25
+ rewind
26
+ read
27
+ end
28
+ end
29
+
30
+ def exception_line(ex)
31
+ idx = ex.backtrace.find_index { |bt| /:in `should'$/ =~ bt }
32
+
33
+ if idx
34
+ line_num_regex = /.*:(\d+)(:.*|$)/
35
+
36
+ err_line = line_num_regex.match(ex.backtrace[0])[1].to_i
37
+ ref_line = line_num_regex.match(ex.backtrace[idx + 1])[1].to_i
38
+
39
+ err_line - ref_line
40
+ else
41
+ raise "Required calling pattern is lambda { xxx }.should raise_error_on_line(...)"
42
+ end
43
+ end
44
+
45
+ def raise_error_on_line(exception, line, &block)
46
+ raise_exception(exception) do |err|
47
+ exception_line(err).should == line
48
+ block.call(err) if block_given?
49
+ end
50
+ end
51
+
52
+ def binary(str)
53
+ if str.respond_to?(:force_encoding)
54
+ str.dup.force_encoding(Encoding::BINARY)
55
+ else
56
+ str
57
+ end
58
+ end
@@ -0,0 +1,300 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "spec_common"))
4
+ require 'bindata/io'
5
+ require 'bindata/string'
6
+
7
+ describe BinData::String, "with mutually exclusive parameters" do
8
+ it ":value and :initial_value" do
9
+ params = {:value => "", :initial_value => ""}
10
+ expect { BinData::String.new(params) }.to raise_error(ArgumentError)
11
+ end
12
+
13
+ it ":length and :read_length" do
14
+ params = {:length => 5, :read_length => 5}
15
+ expect { BinData::String.new(params) }.to raise_error(ArgumentError)
16
+ end
17
+
18
+ it ":value and :length" do
19
+ params = {:value => "", :length => 5}
20
+ expect { BinData::String.new(params) }.to raise_error(ArgumentError)
21
+ end
22
+ end
23
+
24
+ describe BinData::String, "when assigning" do
25
+ let(:small) { BinData::String.new(:length => 3, :pad_byte => "A") }
26
+ let(:large) { BinData::String.new(:length => 5, :pad_byte => "B") }
27
+
28
+ it "copies data from small to large" do
29
+ large.assign(small)
30
+ large.should == "AAABB"
31
+ end
32
+
33
+ it "copies data from large to small" do
34
+ small.assign(large)
35
+ small.should == "BBB"
36
+ end
37
+ end
38
+
39
+ describe BinData::String do
40
+ subject { BinData::String.new("testing") }
41
+
42
+ it "compares with regexp" do
43
+ (/es/ =~ subject).should == 1
44
+ end
45
+
46
+ it "compares with regexp" do
47
+ (subject =~ /es/).should == 1
48
+ end
49
+ end
50
+
51
+ describe BinData::String, "with :read_length" do
52
+ subject { BinData::String.new(:read_length => 5) }
53
+
54
+ its(:num_bytes) { should == 0 }
55
+ its(:value) { should == "" }
56
+
57
+ it "reads :read_length bytes" do
58
+ subject.read("abcdefghij")
59
+ subject.should == "abcde"
60
+ end
61
+
62
+ it "remembers :read_length after value is cleared" do
63
+ subject.assign("abc")
64
+ subject.num_bytes.should == 3
65
+ subject.clear
66
+
67
+ subject.read("abcdefghij")
68
+ subject.should == "abcde"
69
+ end
70
+ end
71
+
72
+ describe BinData::String, "with :length" do
73
+ subject { BinData::String.new(:length => 5) }
74
+
75
+ its(:num_bytes) { should == 5 }
76
+ its(:value) { should == "\0\0\0\0\0" }
77
+
78
+ it "retains :length after value is set" do
79
+ subject.assign("abcdefghij")
80
+ subject.num_bytes.should == 5
81
+ end
82
+
83
+ it "reads :length bytes" do
84
+ subject.read("abcdefghij")
85
+ subject.should == "abcde"
86
+ end
87
+
88
+ it "pads values less than :length" do
89
+ subject.assign("abc")
90
+ subject.should == "abc\0\0"
91
+ end
92
+
93
+ it "accepts values exactly :length" do
94
+ subject.assign("abcde")
95
+ subject.should == "abcde"
96
+ end
97
+
98
+ it "truncates values greater than :length" do
99
+ subject.assign("abcdefghij")
100
+ subject.should == "abcde"
101
+ end
102
+ end
103
+
104
+ describe BinData::String, "with :read_length and :initial_value" do
105
+ subject { BinData::String.new(:read_length => 5, :initial_value => "abcdefghij") }
106
+
107
+ its(:num_bytes) { should == 10 }
108
+ its(:value) { should == "abcdefghij" }
109
+
110
+ it "uses :read_length for reading" do
111
+ io = StringIO.new("ABCDEFGHIJKLMNOPQRST")
112
+ subject.read(io)
113
+ io.pos.should == 5
114
+ end
115
+
116
+ it "forgets :initial_value after reading" do
117
+ subject.read("ABCDEFGHIJKLMNOPQRST")
118
+ subject.num_bytes.should == 5
119
+ subject.should == "ABCDE"
120
+ end
121
+ end
122
+
123
+ describe BinData::String, "with :read_length and :value" do
124
+ subject { BinData::String.new(:read_length => 5, :value => "abcdefghij") }
125
+
126
+ its(:num_bytes) { should == 10 }
127
+ its(:value) { should == "abcdefghij" }
128
+
129
+ it "uses :read_length for reading" do
130
+ io = StringIO.new("ABCDEFGHIJKLMNOPQRST")
131
+ subject.read(io)
132
+ io.pos.should == 5
133
+ end
134
+
135
+ context "after reading" do
136
+ before(:each) do
137
+ subject.read("ABCDEFGHIJKLMNOPQRST")
138
+ end
139
+
140
+ it "is not affected by :read_length after reading" do
141
+ subject.num_bytes.should == 10
142
+ subject.should == "abcdefghij"
143
+ end
144
+
145
+ it "returns read value while reading" do
146
+ subject.stub(:reading?).and_return(true)
147
+ subject.should == "ABCDE"
148
+ end
149
+ end
150
+ end
151
+
152
+ describe BinData::String, "with :length and :initial_value" do
153
+ subject { BinData::String.new(:length => 5, :initial_value => "abcdefghij") }
154
+
155
+ its(:num_bytes) { should == 5 }
156
+ its(:value) { should == "abcde" }
157
+
158
+ it "forgets :initial_value after reading" do
159
+ io = StringIO.new("ABCDEFGHIJKLMNOPQRST")
160
+ subject.read(io)
161
+ io.pos.should == 5
162
+ subject.num_bytes.should == 5
163
+ subject.should == "ABCDE"
164
+ end
165
+ end
166
+
167
+ describe BinData::String, "with :pad_byte" do
168
+ it "accepts a numeric value for :pad_byte" do
169
+ str = BinData::String.new(:length => 5, :pad_byte => 6)
170
+ str.assign("abc")
171
+ str.should == "abc\x06\x06"
172
+ end
173
+
174
+ it "accepts a character for :pad_byte" do
175
+ str = BinData::String.new(:length => 5, :pad_byte => "R")
176
+ str.assign("abc")
177
+ str.should == "abcRR"
178
+ end
179
+
180
+ it "does not accept a string for :pad_byte" do
181
+ params = {:length => 5, :pad_byte => "RR"}
182
+ lambda { BinData::String.new(params) }.should raise_error(ArgumentError)
183
+ end
184
+ end
185
+
186
+ describe BinData::String, "with :trim_padding" do
187
+ it "set false is the default" do
188
+ str1 = BinData::String.new(:length => 5)
189
+ str2 = BinData::String.new(:length => 5, :trim_padding => false)
190
+ str1.assign("abc")
191
+ str2.assign("abc")
192
+ str1.should == "abc\0\0"
193
+ str2.should == "abc\0\0"
194
+ end
195
+
196
+ context "trim padding set" do
197
+ subject { BinData::String.new(:pad_byte => 'R', :trim_padding => true) }
198
+
199
+ it "trims the value" do
200
+ subject.assign("abcRR")
201
+ subject.should == "abc"
202
+ end
203
+
204
+ it "does not affect num_bytes" do
205
+ subject.assign("abcRR")
206
+ subject.num_bytes.should == 5
207
+ end
208
+
209
+ it "trims if last char is :pad_byte" do
210
+ subject.assign("abcRR")
211
+ subject.should == "abc"
212
+ end
213
+
214
+ it "does not trim if value contains :pad_byte not at the end" do
215
+ subject.assign("abcRRde")
216
+ subject.should == "abcRRde"
217
+ end
218
+ end
219
+ end
220
+
221
+ describe BinData::String, "with :pad_front" do
222
+ it "set false is the default" do
223
+ str1 = BinData::String.new(:length => 5)
224
+ str2 = BinData::String.new(:length => 5, :pad_front => false)
225
+ str1.assign("abc")
226
+ str2.assign("abc")
227
+ str1.should == "abc\0\0"
228
+ str2.should == "abc\0\0"
229
+ end
230
+
231
+ it "pads to the front" do
232
+ str = BinData::String.new(:length => 5, :pad_byte => 'R', :pad_front => true)
233
+ str.assign("abc")
234
+ str.should == "RRabc"
235
+ end
236
+
237
+ it "can alternatively be accesses by :pad_left" do
238
+ str = BinData::String.new(:length => 5, :pad_byte => 'R', :pad_left => true)
239
+ str.assign("abc")
240
+ str.should == "RRabc"
241
+ end
242
+
243
+ context "and :trim_padding" do
244
+ subject { BinData::String.new(:length => 5, :pad_byte => 'R', :pad_front => true, :trim_padding => true) }
245
+
246
+ it "assigns" do
247
+ subject.assign("abc")
248
+ subject.should == "abc"
249
+ end
250
+
251
+ it "has to_binary_s" do
252
+ subject.assign("abc")
253
+ subject.to_binary_s.should == "RRabc"
254
+ end
255
+
256
+ it "reads" do
257
+ subject.read "RRabc"
258
+ subject.should == "abc"
259
+ end
260
+ end
261
+ end
262
+
263
+ describe BinData::String, "with Ruby 1.9 encodings" do
264
+ if RUBY_VERSION >= "1.9"
265
+ class UTF8String < BinData::String
266
+ def snapshot
267
+ super.force_encoding('UTF-8')
268
+ end
269
+ end
270
+
271
+ subject { UTF8String.new }
272
+ let(:binary_str) { binary("\xC3\x85\xC3\x84\xC3\x96") }
273
+ let(:utf8_str) { binary_str.dup.force_encoding('UTF-8') }
274
+
275
+ it "stores assigned values as binary" do
276
+ subject.assign(utf8_str)
277
+ subject.to_binary_s.should == binary_str
278
+ end
279
+
280
+ it "stores read values as binary" do
281
+ subject = UTF8String.new(:read_length => binary_str.length)
282
+ subject.read(binary_str)
283
+
284
+ subject.to_binary_s.should == binary_str
285
+ end
286
+
287
+ it "returns values in correct encoding" do
288
+ subject.assign(utf8_str)
289
+
290
+ subject.snapshot.should == utf8_str
291
+ end
292
+
293
+ it "has correct num_bytes" do
294
+ subject.assign(utf8_str)
295
+
296
+ subject.num_bytes.should == binary_str.length
297
+ end
298
+ end
299
+ end
300
+