bindata 0.6.0 → 0.7.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.
- data/ChangeLog +7 -0
- data/README +4 -0
- data/TODO +0 -5
- data/lib/bindata.rb +1 -1
- data/lib/bindata/array.rb +96 -11
- data/lib/bindata/base.rb +16 -13
- data/lib/bindata/lazy.rb +32 -28
- data/lib/bindata/single.rb +9 -6
- data/lib/bindata/struct.rb +1 -2
- data/spec/array_spec.rb +124 -25
- data/spec/base_spec.rb +34 -34
- data/spec/choice_spec.rb +11 -11
- data/spec/float_spec.rb +16 -16
- data/spec/int_spec.rb +6 -6
- data/spec/lazy_spec.rb +58 -54
- data/spec/registry_spec.rb +9 -9
- data/spec/single_spec.rb +41 -41
- data/spec/spec_common.rb +1 -1
- data/spec/string_spec.rb +41 -41
- data/spec/stringz_spec.rb +32 -32
- data/spec/struct_spec.rb +33 -33
- metadata +2 -2
data/ChangeLog
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
= BinData Changelog
|
2
2
|
|
3
|
+
== Version 0.7.0 (2007-08-26)
|
4
|
+
|
5
|
+
* Arrays now support terminating conditions as well as fixed length reads.
|
6
|
+
* Updated specs to new rspec syntax (0.9).
|
7
|
+
* Added scoped resolution of variables in lambdas.
|
8
|
+
* Added ability to append elements to arrays.
|
9
|
+
|
3
10
|
== Version 0.6.0 (2007-03-28)
|
4
11
|
|
5
12
|
* Added 64 bit integers.
|
data/README
CHANGED
@@ -157,12 +157,16 @@ BinData::Int16le:: Signed 16 bit integer (little endian).
|
|
157
157
|
BinData::Int16be:: Signed 16 bit integer (big endian).
|
158
158
|
BinData::Int32le:: Signed 32 bit integer (little endian).
|
159
159
|
BinData::Int32be:: Signed 32 bit integer (big endian).
|
160
|
+
BinData::Int64le:: Signed 64 bit integer (little endian).
|
161
|
+
BinData::Int64be:: Signed 64 bit integer (big endian).
|
160
162
|
|
161
163
|
BinData::Uint8:: Unsigned 8 bit integer.
|
162
164
|
BinData::Uint16le:: Unsigned 16 bit integer (little endian).
|
163
165
|
BinData::Uint16be:: Unsigned 16 bit integer (big endian).
|
164
166
|
BinData::Uint32le:: Unsigned 32 bit integer (little endian).
|
165
167
|
BinData::Uint32be:: Unsigned 32 bit integer (big endian).
|
168
|
+
BinData::Uint64le:: Unsigned 64 bit integer (little endian).
|
169
|
+
BinData::Uint64be:: Unsigned 64 bit integer (big endian).
|
166
170
|
|
167
171
|
BinData::FloatLe:: Single precision floating point number (little endian).
|
168
172
|
BinData::FloatBe:: Single precision floating point number (big endian).
|
data/TODO
CHANGED
@@ -1,8 +1,3 @@
|
|
1
|
-
* Add a way to do something like: read a bunch of integers and stop reading
|
2
|
-
after reading an integer with a value of 0.
|
3
|
-
|
4
|
-
* Add scoping so the value of a param doesn't need to call parent
|
5
|
-
|
6
1
|
* Think how offset_of should work.
|
7
2
|
|
8
3
|
* Need optional parameter for fields
|
data/lib/bindata.rb
CHANGED
data/lib/bindata/array.rb
CHANGED
@@ -21,6 +21,15 @@ module BinData
|
|
21
21
|
# passed to it, then it should be provided as
|
22
22
|
# <tt>[type_symbol, hash_params]</tt>.
|
23
23
|
# <tt>:initial_length</tt>:: The initial length of the array.
|
24
|
+
# <tt>:read_until</tt>:: While reading, elements are read until this
|
25
|
+
# condition is true. This is typically used to
|
26
|
+
# read an array until a sentinel value is found.
|
27
|
+
# The variables +index+, +element+ and +array+
|
28
|
+
# are made available to any lambda assigned to
|
29
|
+
# this parameter.
|
30
|
+
#
|
31
|
+
# Each data object in an array has the variable +index+ made available
|
32
|
+
# to any lambda evaluated as a parameter of that data object.
|
24
33
|
class Array < Base
|
25
34
|
include Enumerable
|
26
35
|
|
@@ -28,11 +37,13 @@ module BinData
|
|
28
37
|
register(self.name, self)
|
29
38
|
|
30
39
|
# These are the parameters used by this class.
|
31
|
-
|
40
|
+
mandatory_parameter :type
|
41
|
+
optional_parameters :initial_length, :read_until
|
32
42
|
|
33
43
|
# Creates a new Array
|
34
44
|
def initialize(params = {}, env = nil)
|
35
|
-
super(params, env)
|
45
|
+
super(cleaned_params(params), env)
|
46
|
+
ensure_mutual_exclusion(:initial_length, :read_until)
|
36
47
|
|
37
48
|
type, el_params = param(:type)
|
38
49
|
klass = klass_lookup(type)
|
@@ -41,8 +52,6 @@ module BinData
|
|
41
52
|
@element_list = nil
|
42
53
|
@element_klass = klass
|
43
54
|
@element_params = el_params || {}
|
44
|
-
|
45
|
-
# TODO: how to increase the size of the array?
|
46
55
|
end
|
47
56
|
|
48
57
|
# Clears the element at position +index+. If +index+ is not given, then
|
@@ -73,7 +82,19 @@ module BinData
|
|
73
82
|
|
74
83
|
# Reads the values for all fields in this object from +io+.
|
75
84
|
def _do_read(io)
|
76
|
-
|
85
|
+
if has_param?(:initial_length)
|
86
|
+
elements.each { |f| f.do_read(io) }
|
87
|
+
else # :read_until
|
88
|
+
@element_list = nil
|
89
|
+
loop do
|
90
|
+
element = append_new_element
|
91
|
+
element.do_read(io)
|
92
|
+
variables = { :index => self.length - 1, :element => self.last,
|
93
|
+
:array => self }
|
94
|
+
finished = eval_param(:read_until, variables)
|
95
|
+
break if finished
|
96
|
+
end
|
97
|
+
end
|
77
98
|
end
|
78
99
|
|
79
100
|
# To be called after calling #do_read.
|
@@ -107,6 +128,46 @@ module BinData
|
|
107
128
|
[]
|
108
129
|
end
|
109
130
|
|
131
|
+
# Returns the first element, or the first +n+ elements, of the array.
|
132
|
+
# If the array is empty, the first form returns nil, and the second
|
133
|
+
# form returns an empty array.
|
134
|
+
def first(n = nil)
|
135
|
+
if n.nil?
|
136
|
+
self.length.zero? ? nil : self[0]
|
137
|
+
else
|
138
|
+
array = []
|
139
|
+
[n, self.length].min.times do |i|
|
140
|
+
array.push(self[i])
|
141
|
+
end
|
142
|
+
array
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Returns the last element, or the last +n+ elements, of the array.
|
147
|
+
# If the array is empty, the first form returns nil, and the second
|
148
|
+
# form returns an empty array.
|
149
|
+
def last(n = nil)
|
150
|
+
if n.nil?
|
151
|
+
self.length.zero? ? nil : self[self.length - 1]
|
152
|
+
else
|
153
|
+
array = []
|
154
|
+
start = self.length - [n, self.length].min
|
155
|
+
start.upto(self.length - 1) do |i|
|
156
|
+
array.push(self[i])
|
157
|
+
end
|
158
|
+
array
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Appends a new element to the end of the array. If the array contains
|
163
|
+
# single_values then the +value+ may be provided to the call.
|
164
|
+
# Returns the appended object, or value in the case of single_values.
|
165
|
+
def append(value = nil)
|
166
|
+
append_new_element
|
167
|
+
self[self.length - 1] = value unless value.nil?
|
168
|
+
self.last
|
169
|
+
end
|
170
|
+
|
110
171
|
# Returns the element at +index+. If the element is a single_value
|
111
172
|
# then the value of the element is returned instead.
|
112
173
|
def [](index)
|
@@ -146,15 +207,39 @@ module BinData
|
|
146
207
|
def elements
|
147
208
|
if @element_list.nil?
|
148
209
|
@element_list = []
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
@element_list << @element_klass.new(@element_params, env)
|
210
|
+
if has_param?(:initial_length)
|
211
|
+
# create the desired number of instances
|
212
|
+
eval_param(:initial_length).times do
|
213
|
+
append_new_element
|
214
|
+
end
|
155
215
|
end
|
156
216
|
end
|
157
217
|
@element_list
|
158
218
|
end
|
219
|
+
|
220
|
+
# Creates a new element and appends it to the end of @element_list.
|
221
|
+
# Returns the newly created element
|
222
|
+
def append_new_element
|
223
|
+
# ensure @element_list is initialised
|
224
|
+
elements()
|
225
|
+
|
226
|
+
env = create_env
|
227
|
+
env.add_variable(:index, @element_list.length)
|
228
|
+
element = @element_klass.new(@element_params, env)
|
229
|
+
@element_list << element
|
230
|
+
element
|
231
|
+
end
|
232
|
+
|
233
|
+
# Returns a hash of cleaned +params+. Cleaning means that param
|
234
|
+
# values are converted to a desired format.
|
235
|
+
def cleaned_params(params)
|
236
|
+
unless params.has_key?(:initial_length) or params.has_key?(:read_until)
|
237
|
+
# ensure one of :initial_length and :read_until exists
|
238
|
+
new_params = params.dup
|
239
|
+
new_params[:initial_length] = 0
|
240
|
+
params = new_params
|
241
|
+
end
|
242
|
+
params
|
243
|
+
end
|
159
244
|
end
|
160
245
|
end
|
data/lib/bindata/base.rb
CHANGED
@@ -14,11 +14,13 @@ module BinData
|
|
14
14
|
#
|
15
15
|
# [<tt>:readwrite</tt>] If false, calls to #read or #write will
|
16
16
|
# not perform any I/O. Default is true.
|
17
|
-
# [<tt>:check_offset</tt>] Raise an error if the current IO
|
17
|
+
# [<tt>:check_offset</tt>] Raise an error if the current IO offset doesn't
|
18
18
|
# meet this criteria. A boolean return indicates
|
19
19
|
# success or failure. Any other return is compared
|
20
|
-
# to the current offset.
|
21
|
-
#
|
20
|
+
# to the current offset. The variable +offset+
|
21
|
+
# is made available to any lambda assigned to
|
22
|
+
# this parameter. This parameter is only checked
|
23
|
+
# before reading.
|
22
24
|
class Base
|
23
25
|
class << self
|
24
26
|
# Returns the mandatory parameters used by this class. Any given args
|
@@ -33,7 +35,7 @@ module BinData
|
|
33
35
|
end
|
34
36
|
end
|
35
37
|
end
|
36
|
-
|
38
|
+
if not args.empty?
|
37
39
|
args.each { |arg| @mandatory_parameters << arg.to_sym }
|
38
40
|
@mandatory_parameters.uniq!
|
39
41
|
end
|
@@ -53,7 +55,7 @@ module BinData
|
|
53
55
|
end
|
54
56
|
end
|
55
57
|
end
|
56
|
-
|
58
|
+
if not args.empty?
|
57
59
|
args.each { |arg| @optional_parameters << arg.to_sym }
|
58
60
|
@optional_parameters.uniq!
|
59
61
|
end
|
@@ -111,14 +113,14 @@ module BinData
|
|
111
113
|
# environment that these callable objects are evaluated in.
|
112
114
|
def initialize(params = {}, env = nil)
|
113
115
|
# default :readwrite param to true if unspecified
|
114
|
-
|
116
|
+
if not params.has_key?(:readwrite)
|
115
117
|
params = params.dup
|
116
118
|
params[:readwrite] = true
|
117
119
|
end
|
118
120
|
|
119
121
|
# ensure mandatory parameters exist
|
120
122
|
self.class.mandatory_parameters.each do |prm|
|
121
|
-
|
123
|
+
if not params.has_key?(prm)
|
122
124
|
raise ArgumentError, "parameter ':#{prm}' must be specified " +
|
123
125
|
"in #{self}"
|
124
126
|
end
|
@@ -197,9 +199,10 @@ module BinData
|
|
197
199
|
|
198
200
|
# Returns the value of the evaluated parameter. +key+ references a
|
199
201
|
# parameter from the +params+ hash used when creating the data object.
|
202
|
+
# +values+ contains data that may be accessed when evaluating +key+.
|
200
203
|
# Returns nil if +key+ does not refer to any parameter.
|
201
|
-
def eval_param(key)
|
202
|
-
@env.lazy_eval(@params[key])
|
204
|
+
def eval_param(key, values = {})
|
205
|
+
@env.lazy_eval(@params[key], values)
|
203
206
|
end
|
204
207
|
|
205
208
|
# Returns the parameter from the +params+ hash referenced by +key+.
|
@@ -227,13 +230,13 @@ module BinData
|
|
227
230
|
# be called from #do_read before performing the reading.
|
228
231
|
def check_offset(io)
|
229
232
|
if has_param?(:check_offset)
|
230
|
-
|
231
|
-
expected = eval_param(:check_offset)
|
233
|
+
actual_offset = io.pos - io.mark
|
234
|
+
expected = eval_param(:check_offset, :offset => actual_offset)
|
232
235
|
|
233
236
|
if not expected
|
234
237
|
raise ValidityError, "offset not as expected"
|
235
|
-
elsif
|
236
|
-
raise ValidityError, "offset is '#{
|
238
|
+
elsif actual_offset != expected and expected != true
|
239
|
+
raise ValidityError, "offset is '#{actual_offset}' but " +
|
237
240
|
"expected '#{expected}'"
|
238
241
|
end
|
239
242
|
end
|
data/lib/bindata/lazy.rb
CHANGED
@@ -7,11 +7,8 @@ module BinData
|
|
7
7
|
# params:: any extra parameters that have been passed to the data object.
|
8
8
|
# The value of a parameter is either a lambda, a symbol or a
|
9
9
|
# literal value (such as a Fixnum).
|
10
|
-
# value:: the value of the data object if it is single
|
11
|
-
# index:: the index of the data object if it is in an array
|
12
|
-
# offset:: the current offset of the IO object when reading
|
13
10
|
#
|
14
|
-
# Unknown methods are resolved in the context of the parent
|
11
|
+
# Unknown methods are resolved in the context of the parent environment,
|
15
12
|
# first as keys in the extra parameters, and secondly as methods in the
|
16
13
|
# parent data object. This makes the lambda easier to read as we just write
|
17
14
|
# <tt>field</tt> instead of <tt>obj.field</tt>.
|
@@ -20,21 +17,27 @@ module BinData
|
|
20
17
|
# parent data object.
|
21
18
|
def initialize(parent = nil)
|
22
19
|
@parent = parent
|
20
|
+
@variables = {}
|
21
|
+
@overrides = {}
|
23
22
|
end
|
24
23
|
attr_reader :parent
|
25
|
-
attr_accessor :data_object, :params
|
24
|
+
attr_accessor :data_object, :params
|
26
25
|
|
27
26
|
# only accessible by another LazyEvalEnv
|
28
27
|
protected :data_object
|
29
28
|
|
29
|
+
# Add a variable with a pre-assigned value to this environment. +sym+
|
30
|
+
# will be accessible as a variable for any lambda evaluated
|
31
|
+
# with #lazy_eval.
|
32
|
+
def add_variable(sym, value)
|
33
|
+
@variables[sym.to_sym] = value
|
34
|
+
end
|
35
|
+
|
30
36
|
# TODO: offset_of needs to be better thought out
|
31
37
|
def offset_of(sym)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
else
|
36
|
-
nil
|
37
|
-
end
|
38
|
+
@parent.data_object.offset_of(sym)
|
39
|
+
rescue
|
40
|
+
nil
|
38
41
|
end
|
39
42
|
|
40
43
|
# Returns the data_object for the parent environment.
|
@@ -42,32 +45,33 @@ module BinData
|
|
42
45
|
@parent.nil? ? nil : @parent.data_object
|
43
46
|
end
|
44
47
|
|
45
|
-
# Returns the value of the data object wrapped by this environment.
|
46
|
-
def value
|
47
|
-
@data_object.respond_to?(:value) ? @data_object.value : nil
|
48
|
-
end
|
49
|
-
|
50
48
|
# Evaluates +obj+ in the context of this environment. Evaluation
|
51
49
|
# recurses until it yields a value that is not a symbol or lambda.
|
52
|
-
def lazy_eval(obj)
|
50
|
+
def lazy_eval(obj, overrides = {})
|
51
|
+
@overrides = overrides
|
53
52
|
if obj.is_a? Symbol
|
54
53
|
# treat :foo as lambda { foo }
|
55
|
-
|
54
|
+
obj = __send__(obj)
|
56
55
|
elsif obj.respond_to? :arity
|
57
|
-
instance_eval(&obj)
|
58
|
-
else
|
59
|
-
obj
|
56
|
+
obj = instance_eval(&obj)
|
60
57
|
end
|
58
|
+
@overrides = {}
|
59
|
+
obj
|
61
60
|
end
|
62
61
|
|
63
62
|
def method_missing(symbol, *args)
|
64
|
-
if @
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
@parent.
|
63
|
+
if @overrides.include?(symbol)
|
64
|
+
@overrides[symbol]
|
65
|
+
elsif @variables.include?(symbol)
|
66
|
+
@variables[symbol]
|
67
|
+
elsif @parent
|
68
|
+
obj = symbol
|
69
|
+
if @parent.params and @parent.params.has_key?(symbol)
|
70
|
+
obj = @parent.params[symbol]
|
71
|
+
elsif @parent.data_object and @parent.data_object.respond_to?(symbol)
|
72
|
+
obj = @parent.data_object.__send__(symbol, *args)
|
73
|
+
end
|
74
|
+
@parent.lazy_eval(obj)
|
71
75
|
else
|
72
76
|
super
|
73
77
|
end
|
data/lib/bindata/single.rb
CHANGED
@@ -20,9 +20,11 @@ module BinData
|
|
20
20
|
# will return the value of the data read from the
|
21
21
|
# IO, not the result of the <tt>:value</tt> param.
|
22
22
|
# [<tt>:check_value</tt>] Raise an error unless the value read in meets
|
23
|
-
# this criteria.
|
24
|
-
#
|
25
|
-
#
|
23
|
+
# this criteria. The variable +value+ is made
|
24
|
+
# available to any lambda assigned to this
|
25
|
+
# parameter. A boolean return indicates success
|
26
|
+
# or failure. Any other return is compared to
|
27
|
+
# the value just read in.
|
26
28
|
class Single < Base
|
27
29
|
# These are the parameters used by this class.
|
28
30
|
optional_parameters :initial_value, :value, :check_value
|
@@ -56,11 +58,12 @@ module BinData
|
|
56
58
|
|
57
59
|
# does the value meet expectations?
|
58
60
|
if has_param?(:check_value)
|
59
|
-
|
61
|
+
current_value = self.value
|
62
|
+
expected = eval_param(:check_value, :value => current_value)
|
60
63
|
if not expected
|
61
64
|
raise ValidityError, "value not as expected"
|
62
|
-
elsif
|
63
|
-
raise ValidityError, "value is '#{
|
65
|
+
elsif current_value != expected and expected != true
|
66
|
+
raise ValidityError, "value is '#{current_value}' but " +
|
64
67
|
"expected '#{expected}'"
|
65
68
|
end
|
66
69
|
end
|
data/lib/bindata/struct.rb
CHANGED
@@ -56,8 +56,7 @@ module BinData
|
|
56
56
|
end
|
57
57
|
|
58
58
|
# Returns or sets the endianess of numerics used in this stucture.
|
59
|
-
# Endianess is
|
60
|
-
# in a nested Struct.
|
59
|
+
# Endianess is applied to the fields of this structure.
|
61
60
|
# Valid values are :little and :big.
|
62
61
|
def endian(endian = nil)
|
63
62
|
@endian ||= nil
|
data/spec/array_spec.rb
CHANGED
@@ -5,87 +5,140 @@ require 'bindata/array'
|
|
5
5
|
require 'bindata/int'
|
6
6
|
require 'bindata/struct'
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
describe "Instantiating an Array" do
|
9
|
+
it "should ensure mandatory parameters are supplied" do
|
10
10
|
args = {}
|
11
11
|
lambda { BinData::Array.new(args) }.should raise_error(ArgumentError)
|
12
|
-
args = {:type => :int8}
|
13
|
-
lambda { BinData::Array.new(args) }.should raise_error(ArgumentError)
|
14
12
|
args = {:initial_length => 3}
|
15
13
|
lambda { BinData::Array.new(args) }.should raise_error(ArgumentError)
|
16
14
|
end
|
17
15
|
|
18
|
-
|
16
|
+
it "should fail if a given type is unknown" do
|
19
17
|
args = {:type => :does_not_exist, :initial_length => 3}
|
20
18
|
lambda { BinData::Array.new(args) }.should raise_error(TypeError)
|
21
19
|
end
|
20
|
+
|
21
|
+
it "should not allow both :initial_length and :read_until" do
|
22
|
+
args = {:initial_length => 3, :read_until => lambda { false } }
|
23
|
+
lambda { BinData::Array.new(args) }.should raise_error(ArgumentError)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "An Array with no elements" do
|
28
|
+
before(:each) do
|
29
|
+
@data = BinData::Array.new(:type => :int8)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return correct length" do
|
33
|
+
@data.length.should be_zero
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should return nil for the first element" do
|
37
|
+
@data.first.should be_nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should return [] for the first n elements" do
|
41
|
+
@data.first(3).should eql([])
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should return nil for the last element" do
|
45
|
+
@data.last.should be_nil
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should return [] for the last n elements" do
|
49
|
+
@data.last(3).should eql([])
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should append an element" do
|
53
|
+
@data.append(99)
|
54
|
+
@data.length.should eql(1)
|
55
|
+
@data.last.should eql(99)
|
56
|
+
end
|
22
57
|
end
|
23
58
|
|
24
|
-
|
25
|
-
|
59
|
+
describe "An Array with several elements" do
|
60
|
+
before(:each) do
|
26
61
|
type = [:int16le, {:initial_value => lambda { index + 1 }}]
|
27
62
|
@data = BinData::Array.new(:type => type, :initial_length => 5)
|
28
63
|
end
|
29
64
|
|
30
|
-
|
65
|
+
it "should return a correct snapshot" do
|
31
66
|
@data.snapshot.should eql([1, 2, 3, 4, 5])
|
32
67
|
end
|
33
68
|
|
34
|
-
|
69
|
+
it "should return the first element" do
|
70
|
+
@data.first.should eql(1)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should return the first n elements" do
|
74
|
+
@data.first(3).should eql([1, 2, 3])
|
75
|
+
@data.first(99).should eql([1, 2, 3, 4, 5])
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should return the last element" do
|
79
|
+
@data.last.should eql(5)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should return the last n elements" do
|
83
|
+
@data.last(3).should eql([3, 4, 5])
|
84
|
+
@data.last(99).should eql([1, 2, 3, 4, 5])
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should have correct num elements" do
|
35
88
|
@data.length.should eql(5)
|
36
89
|
@data.size.should eql(5)
|
37
90
|
end
|
38
91
|
|
39
|
-
|
92
|
+
it "should have correct num_bytes" do
|
40
93
|
@data.num_bytes.should eql(10)
|
41
94
|
end
|
42
95
|
|
43
|
-
|
96
|
+
it "should have correct num_bytes for individual elements" do
|
44
97
|
@data.num_bytes(0).should eql(2)
|
45
98
|
end
|
46
99
|
|
47
|
-
|
100
|
+
it "should have no field_names" do
|
48
101
|
@data.field_names.should be_empty
|
49
102
|
end
|
50
103
|
|
51
|
-
|
104
|
+
it "should be able to directly access elements" do
|
52
105
|
@data[1] = 8
|
53
106
|
@data[1].should eql(8)
|
54
107
|
end
|
55
108
|
|
56
|
-
|
109
|
+
it "should be able to use methods from Enumerable" do
|
57
110
|
@data.select { |x| (x % 2) == 0 }.should eql([2, 4])
|
58
111
|
end
|
59
112
|
|
60
|
-
|
113
|
+
it "should clear" do
|
61
114
|
@data[1] = 8
|
62
115
|
@data.clear
|
63
116
|
@data.collect.should eql([1, 2, 3, 4, 5])
|
64
117
|
end
|
65
118
|
|
66
|
-
|
119
|
+
it "should clear a single element" do
|
67
120
|
@data[1] = 8
|
68
121
|
@data.clear(1)
|
69
122
|
@data[1].should eql(2)
|
70
123
|
end
|
71
124
|
|
72
|
-
|
125
|
+
it "should be clear upon creation" do
|
73
126
|
@data.clear?.should be_true
|
74
127
|
end
|
75
128
|
|
76
|
-
|
129
|
+
it "should be clear if all elements are clear" do
|
77
130
|
@data[1] = 8
|
78
131
|
@data.clear(1)
|
79
132
|
@data.clear?.should be_true
|
80
133
|
end
|
81
134
|
|
82
|
-
|
135
|
+
it "should test clear status of individual elements" do
|
83
136
|
@data[1] = 8
|
84
137
|
@data.clear?(0).should be_true
|
85
138
|
@data.clear?(1).should be_false
|
86
139
|
end
|
87
140
|
|
88
|
-
|
141
|
+
it "should read and write correctly" do
|
89
142
|
io = StringIO.new
|
90
143
|
@data[1] = 8
|
91
144
|
@data.write(io)
|
@@ -97,25 +150,71 @@ context "An Array with several elements" do
|
|
97
150
|
@data.read(io)
|
98
151
|
@data[1].should eql(8)
|
99
152
|
end
|
153
|
+
|
154
|
+
it "should append an element" do
|
155
|
+
@data.append(99)
|
156
|
+
@data.length.should eql(6)
|
157
|
+
@data.last.should eql(99)
|
158
|
+
end
|
100
159
|
end
|
101
160
|
|
102
|
-
|
103
|
-
|
161
|
+
describe "An Array containing structs" do
|
162
|
+
before(:each) do
|
104
163
|
type = [:struct, {:fields => [[:int8, :a,
|
105
164
|
{:initial_value => lambda { parent.index }}],
|
106
165
|
[:int8, :b]]}]
|
107
166
|
@data = BinData::Array.new(:type => type, :initial_length => 5)
|
108
167
|
end
|
109
168
|
|
110
|
-
|
169
|
+
it "should access elements, not values" do
|
111
170
|
@data[3].a.should eql(3)
|
112
171
|
end
|
113
172
|
|
114
|
-
|
173
|
+
it "should not be able to modify elements" do
|
115
174
|
lambda { @data[1] = 3 }.should raise_error(NoMethodError)
|
116
175
|
end
|
117
176
|
|
118
|
-
|
177
|
+
it "should interate over each element" do
|
119
178
|
@data.collect { |s| s.a }.should eql([0, 1, 2, 3, 4])
|
120
179
|
end
|
180
|
+
|
181
|
+
it "should be able to append elements" do
|
182
|
+
obj = @data.append
|
183
|
+
obj.a = 3
|
184
|
+
obj.b = 5
|
185
|
+
|
186
|
+
@data.last.a.should eql(3)
|
187
|
+
@data.last.b.should eql(5)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "An Array with :read_until containing +element+" do
|
192
|
+
before(:each) do
|
193
|
+
read_until = lambda { element == 5 }
|
194
|
+
@data = BinData::Array.new(:type => :int8, :read_until => read_until)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "should append to an empty array" do
|
198
|
+
@data.append(3)
|
199
|
+
@data.first.should eql(3)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should read until the sentinel is reached" do
|
203
|
+
io = StringIO.new("\x01\x02\x03\x04\x05\x06\x07")
|
204
|
+
@data.read(io)
|
205
|
+
@data.length.should eql(5)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
describe "An Array with :read_until containing +array+ and +index+" do
|
210
|
+
before(:each) do
|
211
|
+
read_until = lambda { index >=2 and array[index - 2] == 5 }
|
212
|
+
@data = BinData::Array.new(:type => :int8, :read_until => read_until)
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should read until the sentinel is reached" do
|
216
|
+
io = StringIO.new("\x01\x02\x03\x04\x05\x06\x07\x08")
|
217
|
+
@data.read(io)
|
218
|
+
@data.length.should eql(7)
|
219
|
+
end
|
121
220
|
end
|