bindata 1.6.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bindata might be problematic. Click here for more details.

@@ -89,6 +89,12 @@ module BinData
89
89
  end
90
90
  end
91
91
 
92
+ def assign(val)
93
+ super(val)
94
+ set(_value)
95
+ @value = get
96
+ end
97
+
92
98
  def debug_name_of(child) #:nodoc:
93
99
  debug_name + "-internal-"
94
100
  end
@@ -3,42 +3,6 @@ require 'bindata/sanitize'
3
3
  require 'bindata/struct'
4
4
 
5
5
  module BinData
6
- # Extracts args for Records.
7
- #
8
- # Foo.new(:bar => "baz) is ambiguous as to whether :bar is a value or parameter.
9
- #
10
- # BaseArgExtractor always assumes :bar is parameter. This extractor correctly
11
- # identifies it as value or parameter.
12
- class RecordArgExtractor
13
- class << self
14
- def extract(the_class, the_args)
15
- value, parameters, parent = BaseArgExtractor.extract(the_class, the_args)
16
-
17
- if parameters_is_value?(the_class, value, parameters)
18
- value = parameters
19
- parameters = {}
20
- end
21
-
22
- [value, parameters, parent]
23
- end
24
-
25
- def parameters_is_value?(the_class, value, parameters)
26
- if value.nil? and parameters.length > 0
27
- field_names_in_parameters?(the_class, parameters)
28
- else
29
- false
30
- end
31
- end
32
-
33
- def field_names_in_parameters?(the_class, parameters)
34
- field_names = the_class.fields.field_names
35
- param_keys = parameters.keys
36
-
37
- (field_names & param_keys).length > 0
38
- end
39
- end
40
- end
41
-
42
6
  # A Record is a declarative wrapper around Struct.
43
7
  #
44
8
  # require 'bindata'
@@ -68,7 +32,7 @@ module BinData
68
32
  class << self
69
33
 
70
34
  def arg_extractor
71
- RecordArgExtractor
35
+ MultiFieldArgExtractor
72
36
  end
73
37
 
74
38
  def sanitize_parameters!(params) #:nodoc:
@@ -18,28 +18,29 @@ module BinData
18
18
  def register(name, class_to_register)
19
19
  return if class_to_register.nil?
20
20
 
21
- formatted_name = lookup_key(name)
21
+ formatted_name = underscore_name(name)
22
22
  warn_if_name_is_already_registered(formatted_name, class_to_register)
23
23
 
24
24
  @registry[formatted_name] = class_to_register
25
25
  end
26
26
 
27
27
  def unregister(name)
28
- formatted_name = lookup_key(name)
29
- @registry.delete(formatted_name)
28
+ @registry.delete(underscore_name(name))
30
29
  end
31
30
 
32
31
  def lookup(name, endian = nil)
33
- key = lookup_key(name, endian)
34
- try_registering_key(key) unless @registry.has_key?(key)
35
-
32
+ key = normalize_name(name, endian)
36
33
  @registry[key] || raise(UnRegisteredTypeError, name.to_s)
37
34
  end
38
35
 
39
36
  def normalize_name(name, endian = nil)
40
- if lookup(name, endian)
41
- lookup_key(name, endian)
42
- end
37
+ name = underscore_name(name)
38
+ return name if is_registered?(name)
39
+
40
+ name = name_with_endian(name, endian)
41
+ return name if is_registered?(name)
42
+
43
+ name
43
44
  end
44
45
 
45
46
  # Convert CamelCase +name+ to underscore style.
@@ -54,25 +55,28 @@ module BinData
54
55
  #---------------
55
56
  private
56
57
 
57
- def lookup_key(name, endian = nil)
58
- name = underscore_name(name)
58
+ def name_with_endian(name, endian)
59
+ return name if endian.nil?
59
60
 
60
- result = name
61
- if endian != nil
62
- if /^u?int\d+$/ =~ name
63
- result = name + ((endian == :little) ? "le" : "be")
64
- elsif /^(float|double)$/ =~ name
65
- result = name + ((endian == :little) ? "_le" : "_be")
66
- end
61
+ suffix = (endian == :little) ? "le" : "be"
62
+ if /^u?int\d+$/ =~ name
63
+ name + suffix
64
+ else
65
+ name + "_" + suffix
67
66
  end
68
- result
69
67
  end
70
68
 
71
- def try_registering_key(key)
72
- if /^u?int\d+(le|be)$/ =~ key or /^bit\d+(le)?$/ =~ key
73
- class_name = key.gsub(/(?:^|_)(.)/) { $1.upcase }
69
+ def is_registered?(name)
70
+ register_dynamic_class(name) unless @registry.has_key?(name)
71
+
72
+ @registry.has_key?(name)
73
+ end
74
+
75
+ def register_dynamic_class(name)
76
+ if /^u?int\d+(le|be)$/ =~ name or /^bit\d+(le)?$/ =~ name
77
+ class_name = name.gsub(/(?:^|_)(.)/) { $1.upcase }
74
78
  begin
75
- register(key, BinData::const_get(class_name))
79
+ BinData::const_get(class_name)
76
80
  rescue NameError
77
81
  end
78
82
  end
@@ -1,3 +1,3 @@
1
1
  module BinData
2
- VERSION = "1.6.0"
2
+ VERSION = "1.8.0"
3
3
  end
@@ -1,4 +1,4 @@
1
- require "bindata/base_primitive"
1
+ require "bindata/base"
2
2
 
3
3
  module BinData
4
4
  # A virtual field is one that is neither read, written nor occupies space.
@@ -17,31 +17,37 @@ module BinData
17
17
  # obj.a #=> "abcde"
18
18
  # obj.c.offset #=> 10
19
19
  #
20
- class Virtual < BinData::BasePrimitive
21
-
22
- default_parameter :onlyif => false
20
+ # obj = A.read("abcdeABCDE") #=> BinData::ValidityError: assertion failed for obj.c
21
+ #
22
+ # == Parameters
23
+ #
24
+ # Parameters may be provided at initialisation to control the behaviour of
25
+ # an object. These params include those for BinData::Base as well as:
26
+ #
27
+ # [<tt>:assert</tt>] Raise an error when reading or assigning if the value
28
+ # of this evaluated parameter is false.
29
+ #
30
+ class Virtual < BinData::Base
23
31
 
24
- class << self
25
- def sanitize_parameters!(params) #:nodoc:
26
- if params.has_parameter?(:asserted_value)
27
- fail ":asserted_value can not be used on virtual field"
28
- end
29
- end
30
- end
32
+ optional_parameter :assert
31
33
 
32
- #---------------
33
- private
34
+ def clear?; true; end
35
+ def snapshot; nil; end
36
+ def do_num_bytes; 0; end
37
+ def do_write(io); end
34
38
 
35
- def value_to_binary_string(val)
36
- ""
39
+ def assign(val)
40
+ assert!
37
41
  end
38
42
 
39
- def read_and_return_value(io)
40
- ""
43
+ def do_read(io)
44
+ assert!
41
45
  end
42
46
 
43
- def sensible_default
44
- ""
47
+ def assert!
48
+ if has_parameter?(:assert) and not eval_parameter(:assert)
49
+ raise ValidityError, "assertion failed for #{debug_name}"
50
+ end
45
51
  end
46
52
  end
47
53
  end
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "common"))
4
+
5
+ describe BinData::Buffer, "when instantiating" do
6
+ describe "with no mandatory parameters supplied" do
7
+ it "raises an error" do
8
+ args = {}
9
+ lambda { BinData::Buffer.new(args) }.must_raise ArgumentError
10
+ end
11
+ end
12
+
13
+ describe "with some but not all mandatory parameters supplied" do
14
+ it "raises an error" do
15
+ args = {:length => 3}
16
+ lambda { BinData::Buffer.new(args) }.must_raise ArgumentError
17
+ end
18
+ end
19
+
20
+ it "fails if a given type is unknown" do
21
+ args = {:type => :does_not_exist, :length => 3}
22
+ lambda { BinData::Buffer.new(args) }.must_raise BinData::UnRegisteredTypeError
23
+ end
24
+
25
+ it "accepts BinData::Base as :type" do
26
+ obj = BinData::Int8.new(:initial_value => 5)
27
+ array = BinData::Buffer.new(:type => obj, :length => 3)
28
+ array.must_equal 5
29
+ end
30
+ end
31
+
32
+ describe BinData::Buffer, "subclassed with a single type" do
33
+ class IntBuffer < BinData::Buffer
34
+ endian :big
35
+ default_parameter :length => 5
36
+
37
+ uint16
38
+ end
39
+
40
+ it "behaves as type" do
41
+ obj = IntBuffer.new(3)
42
+ obj.must_equal 3
43
+ end
44
+
45
+ it "reads data" do
46
+ obj = IntBuffer.read "\001\002\003\004\005"
47
+ obj.must_equal 0x0102
48
+ end
49
+
50
+ it "writes data" do
51
+ obj = IntBuffer.new(3)
52
+ obj.to_binary_s.must_equal "\000\003\000\000\000"
53
+ end
54
+
55
+ it "has total num_bytes" do
56
+ obj = IntBuffer.new
57
+ obj.num_bytes.must_equal 5
58
+ end
59
+ end
60
+
61
+ describe BinData::Buffer, "subclassed with multiple types" do
62
+ class TupleBuffer < BinData::Buffer
63
+ endian :big
64
+ default_parameter :length => 5
65
+
66
+ uint16 :a
67
+ uint16 :b
68
+ end
69
+
70
+ it "behaves as type" do
71
+ obj = TupleBuffer.new(:a => 1, :b => 2)
72
+ obj.a.must_equal 1
73
+ obj.b.must_equal 2
74
+ end
75
+
76
+ it "has total num_bytes" do
77
+ obj = TupleBuffer.new
78
+ obj.num_bytes.must_equal 5
79
+ end
80
+
81
+ it "reads data" do
82
+ obj = TupleBuffer.read "\001\002\003\004\005"
83
+ obj.a.must_equal 0x0102
84
+ obj.b.must_equal 0x0304
85
+ end
86
+
87
+ it "writes data" do
88
+ obj = TupleBuffer.new(:a => 1, :b => 2)
89
+ obj.to_binary_s.must_equal "\000\001\000\002\000"
90
+ end
91
+ end
92
+
93
+ describe BinData::Buffer, "inside a Record" do
94
+ class BufferRecord < BinData::Record
95
+ endian :little
96
+
97
+ uint16 :buffer_length, :value => lambda { 2 * list.length + 1 }
98
+ buffer :list, :length => :buffer_length do
99
+ array :type => :int16, :read_until => :eof
100
+ end
101
+ string :footer, :length => 2, :asserted_value => "ZZ"
102
+ end
103
+
104
+ it "reads" do
105
+ obj = BufferRecord.read "\007\000\004\000\005\000\006\000\000ZZ"
106
+ obj.list.must_equal [4, 5, 6]
107
+ end
108
+
109
+ it "writes" do
110
+ obj = BufferRecord.new(:list => [1, 2, 3, 4, 5])
111
+ obj.to_binary_s.must_equal "\013\000\001\000\002\000\003\000\004\000\005\000\000ZZ"
112
+ end
113
+ end
114
+
115
+ describe BinData::Buffer, "nested buffers" do
116
+ class NestedBufferRecord < BinData::Record
117
+ buffer :a, :length => 10 do
118
+ buffer :aa, :length => 5 do
119
+ string :read_length => 5
120
+ end
121
+ buffer :bb, :length => 20 do
122
+ string :read_length => 5
123
+ end
124
+ end
125
+ string :b, :read_length => 5
126
+ end
127
+
128
+ it "restricts large nested buffer" do
129
+ obj = NestedBufferRecord.read "abcdefghijklmnopqrst"
130
+ obj.a.aa.must_equal "abcde"
131
+ obj.a.bb.must_equal "fghij"
132
+ obj.b.must_equal "klmno"
133
+ end
134
+
135
+ it "restricts oversize writes" do
136
+ obj = NestedBufferRecord.new
137
+ obj.a.aa = "abcdefghij"
138
+ obj.a.bb = "ABCDEFGHIJ"
139
+ obj.b = "12345"
140
+
141
+ obj.to_binary_s.must_equal "abcdeABCDE12345"
142
+ end
143
+ end
144
+
@@ -30,7 +30,7 @@ end
30
30
 
31
31
  class ExampleSingle < BinData::BasePrimitive
32
32
  def self.io_with_value(val)
33
- BinData::IO.new([val].pack("V"))
33
+ StringIO.new([val].pack("V"))
34
34
  end
35
35
 
36
36
  private
@@ -2,63 +2,43 @@
2
2
 
3
3
  require File.expand_path(File.join(File.dirname(__FILE__), "common"))
4
4
 
5
- describe BinData::IO, "reading from non seekable stream" do
5
+ describe BinData::IO::Read, "reading from non seekable stream" do
6
6
  before do
7
7
  @rd, @wr = IO::pipe
8
- if fork
9
- # parent
10
- @wr.close
11
- @io = BinData::IO.new(@rd)
12
- else
13
- # child
14
- begin
15
- @rd.close
16
- @wr.write "a" * 5000
17
- @wr.write "b" * 5000
18
- @wr.close
19
- rescue Exception
20
- # ignore it
21
- ensure
22
- exit!
23
- end
24
- end
8
+ @io = BinData::IO::Read.new(@rd)
9
+ @wr.write "a" * 2000
10
+ @wr.write "b" * 2000
11
+ @wr.close
25
12
  end
26
13
 
27
14
  after do
28
15
  @rd.close
29
- Process.wait
30
16
  end
31
17
 
32
- it "always has an offset of 0" do
18
+ it "has correct offset" do
33
19
  @io.readbytes(10)
34
- @io.offset.must_equal 0
20
+ @io.offset.must_equal 10
35
21
  end
36
22
 
37
23
  it "seeks correctly" do
38
- @io.seekbytes(4999)
24
+ @io.seekbytes(1999)
39
25
  @io.readbytes(5).must_equal "abbbb"
40
26
  end
41
27
 
42
- it "returns zero for num bytes remaining" do
43
- @io.num_bytes_remaining.must_equal 0
28
+ it "#num_bytes_remaining raises IOError" do
29
+ lambda {
30
+ @io.num_bytes_remaining
31
+ }.must_raise IOError
44
32
  end
45
33
  end
46
34
 
47
- describe BinData::IO, "when reading" do
35
+ describe BinData::IO::Read, "when reading" do
48
36
  let(:stream) { StringIO.new "abcdefghij" }
49
- let(:io) { BinData::IO.new(stream) }
50
-
51
- it "wraps strings in StringIO" do
52
- io.raw_io.class.must_equal StringIO
53
- end
37
+ let(:io) { BinData::IO::Read.new(stream) }
54
38
 
55
- it "does not wrap IO objects" do
56
- io.raw_io.must_equal stream
57
- end
58
-
59
- it "raises error when io is BinData::IO" do
39
+ it "raises error when io is BinData::IO::Read" do
60
40
  lambda {
61
- BinData::IO.new(BinData::IO.new(""))
41
+ BinData::IO::Read.new(BinData::IO::Read.new(""))
62
42
  }.must_raise ArgumentError
63
43
  end
64
44
 
@@ -100,17 +80,69 @@ describe BinData::IO, "when reading" do
100
80
  end
101
81
  end
102
82
 
103
- describe BinData::IO, "when writing" do
104
- let(:stream) { StringIO.new }
105
- let(:io) { BinData::IO.new(stream) }
83
+ describe BinData::IO::Read, "#with_buffer" do
84
+ let(:stream) { StringIO.new "abcdefghijklmnopqrst" }
85
+ let(:io) { BinData::IO::Read.new(stream) }
86
+
87
+ it "consumes entire buffer on short reads" do
88
+ io.with_buffer(10) do
89
+ io.readbytes(4).must_equal "abcd"
90
+ end
91
+ io.offset.must_equal(10)
92
+ end
93
+
94
+ it "consumes entire buffer on read_all_bytes" do
95
+ io.with_buffer(10) do
96
+ io.read_all_bytes.must_equal "abcdefghij"
97
+ end
98
+ io.offset.must_equal(10)
99
+ end
100
+
101
+ it "restricts large reads" do
102
+ io.with_buffer(10) do
103
+ lambda {
104
+ io.readbytes(15)
105
+ }.must_raise IOError
106
+ end
107
+ end
108
+
109
+ it "is nestable" do
110
+ io.with_buffer(10) do
111
+ io.readbytes(2).must_equal "ab"
112
+ io.with_buffer(5) do
113
+ io.read_all_bytes.must_equal "cdefg"
114
+ end
115
+ io.offset.must_equal(2 + 5)
116
+ end
117
+ io.offset.must_equal(10)
118
+ end
119
+
120
+ it "restricts large nested buffers" do
121
+ io.with_buffer(10) do
122
+ io.readbytes(2).must_equal "ab"
123
+ io.with_buffer(20) do
124
+ io.read_all_bytes.must_equal "cdefghij"
125
+ io.offset.must_equal(10)
126
+ end
127
+ end
128
+ io.offset.must_equal(10)
129
+ end
106
130
 
107
- it "does not wrap IO objects" do
108
- io.raw_io.must_equal stream
131
+ it "restricts large seeks" do
132
+ io.with_buffer(10) do
133
+ io.seekbytes(15)
134
+ end
135
+ io.offset.must_equal(10)
109
136
  end
137
+ end
138
+
139
+ describe BinData::IO::Write, "when writing" do
140
+ let(:stream) { StringIO.new }
141
+ let(:io) { BinData::IO::Write.new(stream) }
110
142
 
111
143
  it "raises error when io is BinData::IO" do
112
144
  lambda {
113
- BinData::IO.new(BinData::IO.new(""))
145
+ BinData::IO::Write.new(BinData::IO::Write.new(""))
114
146
  }.must_raise ArgumentError
115
147
  end
116
148
 
@@ -128,11 +160,43 @@ describe BinData::IO, "when writing" do
128
160
  end
129
161
  end
130
162
 
131
- describe BinData::IO, "reading bits in big endian" do
163
+ describe BinData::IO::Write, "#with_buffer" do
164
+ let(:stream) { StringIO.new }
165
+ let(:io) { BinData::IO::Write.new(stream) }
166
+
167
+ it "pads entire buffer on short reads" do
168
+ io.with_buffer(10) do
169
+ io.writebytes "abcde"
170
+ end
171
+
172
+ stream.value.must_equal "abcde\0\0\0\0\0"
173
+ end
174
+
175
+ it "discards excess on large writes" do
176
+ io.with_buffer(5) do
177
+ io.writebytes "abcdefghij"
178
+ end
179
+
180
+ stream.value.must_equal "abcde"
181
+ end
182
+
183
+ it "is nestable" do
184
+ io.with_buffer(10) do
185
+ io.with_buffer(5) do
186
+ io.writebytes "abc"
187
+ end
188
+ io.writebytes "de"
189
+ end
190
+
191
+ stream.value.must_equal "abc\0\0de\0\0\0"
192
+ end
193
+ end
194
+
195
+ describe BinData::IO::Read, "reading bits in big endian" do
132
196
  let(:b1) { 0b1111_1010 }
133
197
  let(:b2) { 0b1100_1110 }
134
198
  let(:b3) { 0b0110_1010 }
135
- let(:io) { BinData::IO.new([b1, b2, b3].pack("CCC")) }
199
+ let(:io) { BinData::IO::Read.new([b1, b2, b3].pack("CCC")) }
136
200
 
137
201
  it "reads a bitfield less than 1 byte" do
138
202
  io.readbits(3, :big).must_equal 0b111
@@ -174,11 +238,11 @@ describe BinData::IO, "reading bits in big endian" do
174
238
  end
175
239
  end
176
240
 
177
- describe BinData::IO, "reading bits in little endian" do
241
+ describe BinData::IO::Read, "reading bits in little endian" do
178
242
  let(:b1) { 0b1111_1010 }
179
243
  let(:b2) { 0b1100_1110 }
180
244
  let(:b3) { 0b0110_1010 }
181
- let(:io) { BinData::IO.new([b1, b2, b3].pack("CCC")) }
245
+ let(:io) { BinData::IO::Read.new([b1, b2, b3].pack("CCC")) }
182
246
 
183
247
  it "reads a bitfield less than 1 byte" do
184
248
  io.readbits(3, :little).must_equal 0b010
@@ -223,7 +287,7 @@ end
223
287
  class BitWriterHelper
224
288
  def initialize
225
289
  @stringio = BinData::IO.create_string_io
226
- @io = BinData::IO.new(@stringio)
290
+ @io = BinData::IO::Write.new(@stringio)
227
291
  end
228
292
 
229
293
  def writebits(val, nbits, endian)
@@ -241,7 +305,7 @@ class BitWriterHelper
241
305
  end
242
306
  end
243
307
 
244
- describe BinData::IO, "writing bits in big endian" do
308
+ describe BinData::IO::Write, "writing bits in big endian" do
245
309
  let(:io) { BitWriterHelper.new }
246
310
 
247
311
  it "writes a bitfield less than 1 byte" do
@@ -286,7 +350,7 @@ describe BinData::IO, "writing bits in big endian" do
286
350
  end
287
351
  end
288
352
 
289
- describe BinData::IO, "writing bits in little endian" do
353
+ describe BinData::IO::Write, "writing bits in little endian" do
290
354
  let(:io) { BitWriterHelper.new }
291
355
 
292
356
  it "writes a bitfield less than 1 byte" do
@@ -331,17 +395,19 @@ describe BinData::IO, "writing bits in little endian" do
331
395
  end
332
396
  end
333
397
 
334
- describe BinData::IO, "with changing endian" do
398
+ describe BinData::IO::Read, "with changing endian" do
335
399
  it "does not mix different endianess when reading" do
336
400
  b1 = 0b0110_1010
337
401
  b2 = 0b1110_0010
338
402
  str = [b1, b2].pack("CC")
339
- io = BinData::IO.new(str)
403
+ io = BinData::IO::Read.new(str)
340
404
 
341
405
  io.readbits(3, :big).must_equal 0b011
342
406
  io.readbits(4, :little).must_equal 0b0010
343
407
  end
408
+ end
344
409
 
410
+ describe BinData::IO::Write, "with changing endian" do
345
411
  it "does not mix different endianess when writing" do
346
412
  io = BitWriterHelper.new
347
413
  io.writebits(0b110, 3, :big)