bindata 1.4.3 → 1.4.4
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/BSDL +1 -1
- data/ChangeLog +8 -0
- data/NEWS +7 -0
- data/lib/bindata.rb +3 -3
- data/lib/bindata/base.rb +7 -0
- data/lib/bindata/base_primitive.rb +2 -2
- data/lib/bindata/choice.rb +4 -0
- data/lib/bindata/io.rb +1 -1
- data/lib/bindata/lazy.rb +2 -2
- data/lib/bindata/sanitize.rb +9 -0
- data/lib/bindata/string.rb +33 -16
- data/manual.haml +55 -53
- data/manual.md +346 -253
- data/spec/alignment_spec.rb +7 -7
- data/spec/array_spec.rb +67 -66
- data/spec/base_primitive_spec.rb +47 -49
- data/spec/base_spec.rb +70 -73
- data/spec/bits_spec.rb +14 -14
- data/spec/choice_spec.rb +44 -41
- data/spec/count_bytes_remaining_spec.rb +8 -11
- data/spec/deprecated_spec.rb +29 -29
- data/spec/float_spec.rb +12 -12
- data/spec/int_spec.rb +24 -24
- data/spec/io_spec.rb +56 -56
- data/spec/lazy_spec.rb +41 -39
- data/spec/primitive_spec.rb +31 -33
- data/spec/record_spec.rb +55 -57
- data/spec/registry_spec.rb +25 -25
- data/spec/rest_spec.rb +5 -5
- data/spec/skip_spec.rb +3 -3
- data/spec/spec_common.rb +2 -2
- data/spec/string_spec.rb +38 -38
- data/spec/stringz_spec.rb +18 -18
- data/spec/struct_spec.rb +48 -48
- data/spec/system_spec.rb +38 -37
- data/spec/wrapper_spec.rb +21 -23
- data/tasks/pkg.rake +3 -3
- data/tasks/rdoc.rake +1 -1
- data/tasks/rspec.rake +7 -7
- metadata +51 -53
data/manual.md
CHANGED
@@ -40,17 +40,18 @@ manipulating.
|
|
40
40
|
It supports all the common datatypes that are found in structured binary
|
41
41
|
data. Support for dependent and variable length fields is built in.
|
42
42
|
|
43
|
-
Last updated:
|
43
|
+
Last updated: 2012-06-21
|
44
44
|
|
45
45
|
## License
|
46
46
|
|
47
47
|
BinData is released under the same license as Ruby.
|
48
48
|
|
49
|
-
Copyright © 2007 -
|
49
|
+
Copyright © 2007 - 2012 [Dion Mendel](mailto:dion@lostrealm.com)
|
50
50
|
|
51
51
|
## Donate
|
52
52
|
|
53
|
-
Want to donate? My favourite local charity is
|
53
|
+
Want to donate? My favourite local charity is
|
54
|
+
[Perth Raptor Care](http://care.raptor.id.au/help.html#PAL).
|
54
55
|
|
55
56
|
---------------------------------------------------------------------------
|
56
57
|
|
@@ -72,8 +73,8 @@ BinData declarations are easy to read. Here's an example.
|
|
72
73
|
|
73
74
|
class MyFancyFormat < BinData::Record
|
74
75
|
stringz :comment
|
75
|
-
uint8 :
|
76
|
-
array :
|
76
|
+
uint8 :len
|
77
|
+
array :data, :type => :int32be, :initial_length => :len
|
77
78
|
end
|
78
79
|
{:ruby}
|
79
80
|
|
@@ -82,22 +83,21 @@ This fancy format describes the following collection of data:
|
|
82
83
|
`:comment`
|
83
84
|
: A zero terminated string
|
84
85
|
|
85
|
-
`:
|
86
|
-
: An unsigned 8bit integer
|
86
|
+
`:len`
|
87
|
+
: An unsigned 8bit integer
|
87
88
|
|
88
|
-
`:
|
89
|
+
`:data`
|
89
90
|
: A sequence of unsigned 32bit big endian integers. The number of
|
90
|
-
integers is given by the value of `:
|
91
|
+
integers is given by the value of `:len`
|
91
92
|
|
92
93
|
The BinData declaration matches the English description closely.
|
93
94
|
Compare the above declaration with the equivalent `#unpack` code to read
|
94
95
|
such a data record.
|
95
96
|
|
96
97
|
def read_fancy_format(io)
|
97
|
-
comment,
|
98
|
-
|
99
|
-
|
100
|
-
{:comment => comment, :num_ints => num_ints, :some_ints => *some_ints}
|
98
|
+
comment, len, rest = io.read.unpack("Z*Ca*")
|
99
|
+
data = rest.unpack("N#{len}")
|
100
|
+
{:comment => comment, :len => len, :data => *data}
|
101
101
|
end
|
102
102
|
{:ruby}
|
103
103
|
|
@@ -111,147 +111,6 @@ underlying binary data representation.
|
|
111
111
|
|
112
112
|
---------------------------------------------------------------------------
|
113
113
|
|
114
|
-
# Common Operations
|
115
|
-
|
116
|
-
There are operations common to all BinData types, including user defined
|
117
|
-
ones. These are summarised here.
|
118
|
-
|
119
|
-
## Reading and writing
|
120
|
-
|
121
|
-
`::read(io)`
|
122
|
-
|
123
|
-
: Creates a BinData object and reads its value from the given string
|
124
|
-
or `IO`. The newly created object is returned.
|
125
|
-
|
126
|
-
obj = BinData::Int8.read("\xff")
|
127
|
-
obj.snapshot #=> -1
|
128
|
-
{:ruby}
|
129
|
-
|
130
|
-
`#read(io)`
|
131
|
-
|
132
|
-
: Reads and assigns binary data read from `io`.
|
133
|
-
|
134
|
-
obj = BinData::Stringz.new
|
135
|
-
obj.read("string 1\0string 2\0")
|
136
|
-
obj #=> "string 1"
|
137
|
-
{:ruby}
|
138
|
-
|
139
|
-
`#write(io)`
|
140
|
-
|
141
|
-
: Writes the binary data representation of the object to `io`.
|
142
|
-
|
143
|
-
File.open("...", "wb") do |io|
|
144
|
-
obj = BinData::Uint64be.new(568290145640170)
|
145
|
-
obj.write(io)
|
146
|
-
end
|
147
|
-
{:ruby}
|
148
|
-
|
149
|
-
`#to_binary_s`
|
150
|
-
|
151
|
-
: Returns the binary data representation of this object as a string.
|
152
|
-
|
153
|
-
obj = BinData::Uint16be.new(4660)
|
154
|
-
obj.to_binary_s #=> "\022\064"
|
155
|
-
{:ruby}
|
156
|
-
|
157
|
-
## Manipulating
|
158
|
-
|
159
|
-
`#assign(value)`
|
160
|
-
|
161
|
-
: Assigns the given value to this object. `value` can be of the same
|
162
|
-
format as produced by `#snapshot`, or it can be a compatible data
|
163
|
-
object.
|
164
|
-
|
165
|
-
arr = BinData::Array.new(:type => :uint8)
|
166
|
-
arr.assign([1, 2, 3, 4])
|
167
|
-
arr.snapshot #=> [1, 2, 3, 4]
|
168
|
-
{:ruby}
|
169
|
-
|
170
|
-
`#clear`
|
171
|
-
|
172
|
-
: Resets this object to its initial state.
|
173
|
-
|
174
|
-
obj = BinData::Int32be.new(:initial_value => 42)
|
175
|
-
obj.assign(50)
|
176
|
-
obj.clear
|
177
|
-
obj #=> 42
|
178
|
-
{:ruby}
|
179
|
-
|
180
|
-
`#clear?`
|
181
|
-
|
182
|
-
: Returns whether this object is in its initial state.
|
183
|
-
|
184
|
-
arr = BinData::Array.new(:type => :uint16be, :initial_length => 5)
|
185
|
-
arr[3] = 42
|
186
|
-
arr.clear? #=> false
|
187
|
-
|
188
|
-
arr[3].clear
|
189
|
-
arr.clear? #=> true
|
190
|
-
{:ruby}
|
191
|
-
|
192
|
-
## Inspecting
|
193
|
-
|
194
|
-
`#num_bytes`
|
195
|
-
|
196
|
-
: Returns the number of bytes required for the binary data
|
197
|
-
representation of this object.
|
198
|
-
|
199
|
-
arr = BinData::Array.new(:type => :uint16be, :initial_length => 5)
|
200
|
-
arr[0].num_bytes #=> 2
|
201
|
-
arr.num_bytes #=> 10
|
202
|
-
{:ruby}
|
203
|
-
|
204
|
-
`#snapshot`
|
205
|
-
|
206
|
-
: Returns the value of this object as primitive Ruby objects
|
207
|
-
(numerics, strings, arrays and hashs). The output of `#snapshot`
|
208
|
-
may be useful for serialization or as a reduced memory usage
|
209
|
-
representation.
|
210
|
-
|
211
|
-
obj = BinData::Uint8.new(2)
|
212
|
-
obj.class #=> BinData::Uint8
|
213
|
-
obj + 3 #=> 5
|
214
|
-
|
215
|
-
obj.snapshot #=> 2
|
216
|
-
obj.snapshot.class #=> Fixnum
|
217
|
-
{:ruby}
|
218
|
-
|
219
|
-
`#offset`
|
220
|
-
|
221
|
-
: Returns the offset of this object with respect to the most distant
|
222
|
-
ancestor structure it is contained within. This is most likely to
|
223
|
-
be used with arrays and records.
|
224
|
-
|
225
|
-
class Tuple < BinData::Record
|
226
|
-
int8 :a
|
227
|
-
int8 :b
|
228
|
-
end
|
229
|
-
|
230
|
-
arr = BinData::Array.new(:type => :tuple, :initial_length => 3)
|
231
|
-
arr[2].b.offset #=> 5
|
232
|
-
{:ruby}
|
233
|
-
|
234
|
-
`#rel_offset`
|
235
|
-
|
236
|
-
: Returns the offset of this object with respect to the parent
|
237
|
-
structure it is contained within. Compare this to `#offset`.
|
238
|
-
|
239
|
-
class Tuple < BinData::Record
|
240
|
-
int8 :a
|
241
|
-
int8 :b
|
242
|
-
end
|
243
|
-
|
244
|
-
arr = BinData::Array.new(:type => :tuple, :initial_length => 3)
|
245
|
-
arr[2].b.rel_offset #=> 1
|
246
|
-
{:ruby}
|
247
|
-
|
248
|
-
`#inspect`
|
249
|
-
|
250
|
-
: Returns a human readable representation of this object. This is a
|
251
|
-
shortcut to #snapshot.inspect.
|
252
|
-
|
253
|
-
---------------------------------------------------------------------------
|
254
|
-
|
255
114
|
# Records
|
256
115
|
|
257
116
|
The general format of a BinData record declaration is a class containing
|
@@ -288,8 +147,8 @@ Here are some examples of legal values for parameters.
|
|
288
147
|
The simplest case is when the value is a literal value, such as `5`.
|
289
148
|
|
290
149
|
If the value is not a literal, it is expected to be a lambda. The
|
291
|
-
lambda will be evaluated in the context of the parent
|
292
|
-
parent is an instance of `MyName`.
|
150
|
+
lambda will be evaluated in the context of the parent. In this case
|
151
|
+
the parent is an instance of `MyName`.
|
293
152
|
|
294
153
|
If the value is a symbol, it is taken as syntactic sugar for a lambda
|
295
154
|
containing the value of the symbol.
|
@@ -407,28 +266,8 @@ first and the length afterwards.
|
|
407
266
|
|
408
267
|
## Nested Records
|
409
268
|
|
410
|
-
|
411
|
-
|
412
|
-
document this grouping.
|
413
|
-
|
414
|
-
class Coord < BinData::Record
|
415
|
-
endian :little
|
416
|
-
double :x
|
417
|
-
double :z
|
418
|
-
double :y
|
419
|
-
end
|
420
|
-
|
421
|
-
class LabeledCoord < BinData::Record
|
422
|
-
string :label, :length => 20
|
423
|
-
coord :coord
|
424
|
-
end
|
425
|
-
|
426
|
-
pos = LabeledCoord.new(:label => "red leader")
|
427
|
-
pos.coord.assign(:x => 2.0, :y => 0, :z => -1.57)
|
428
|
-
{:ruby}
|
429
|
-
|
430
|
-
BinData supports anonymous nested records. The previous example could
|
431
|
-
also be declared using the `struct` keyword as follows:
|
269
|
+
BinData supports anonymous nested records. The `struct` keyword declares
|
270
|
+
a nested structure that can be used to imply a grouping of related data.
|
432
271
|
|
433
272
|
class LabeledCoord < BinData::Record
|
434
273
|
string :label, :length => 20
|
@@ -440,38 +279,24 @@ also be declared using the `struct` keyword as follows:
|
|
440
279
|
double :y
|
441
280
|
end
|
442
281
|
end
|
443
|
-
{:ruby}
|
444
|
-
|
445
|
-
## Bitfields
|
446
282
|
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
Sometimes a bitfield has unused elements such as
|
451
|
-
|
452
|
-
class RecordWithBitfield < BinData::Record
|
453
|
-
bit1 :foo
|
454
|
-
bit1 :bar
|
455
|
-
bit1 :baz
|
456
|
-
bit5 :unused
|
457
|
-
|
458
|
-
stringz :qux
|
459
|
-
end
|
283
|
+
pos = LabeledCoord.new(:label => "red leader")
|
284
|
+
pos.coord.assign(:x => 2.0, :y => 0, :z => -1.57)
|
460
285
|
{:ruby}
|
461
286
|
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
BinData provides a shortcut to skip to the next byte boundary with the
|
466
|
-
`resume_byte_alignment` keyword.
|
287
|
+
This nested structure can be put in its own class and reused.
|
288
|
+
The above example can also be declared as:
|
467
289
|
|
468
|
-
class
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
290
|
+
class Coord < BinData::Record
|
291
|
+
endian :little
|
292
|
+
double :x
|
293
|
+
double :z
|
294
|
+
double :y
|
295
|
+
end
|
473
296
|
|
474
|
-
|
297
|
+
class LabeledCoord < BinData::Record
|
298
|
+
string :label, :length => 20
|
299
|
+
coord :coord
|
475
300
|
end
|
476
301
|
{:ruby}
|
477
302
|
|
@@ -525,7 +350,7 @@ Examples of individual usage:
|
|
525
350
|
fl * int16 #=> 2557.90320057996
|
526
351
|
{:ruby}
|
527
352
|
|
528
|
-
There are several parameters that are specific to primitives.
|
353
|
+
There are several parameters that are specific to all primitives.
|
529
354
|
|
530
355
|
`:initial_value`
|
531
356
|
|
@@ -548,12 +373,23 @@ There are several parameters that are specific to primitives.
|
|
548
373
|
pi = BinData::FloatLe.new(:value => Math::PI)
|
549
374
|
pi.assign(3)
|
550
375
|
puts pi #=> 3.14159265358979
|
376
|
+
|
377
|
+
|
378
|
+
class IntList < BinData::Record
|
379
|
+
uint8 :len, :value => lambda { data.length }
|
380
|
+
array :data, :type => :uint32be
|
381
|
+
end
|
382
|
+
|
383
|
+
list = IntList.new([1, 2, 3])
|
384
|
+
list.len #=> 3
|
551
385
|
{:ruby}
|
552
386
|
|
553
387
|
`:check_value`
|
554
388
|
|
555
389
|
: When reading, will raise a `ValidityError` if the value read does
|
556
|
-
not match the value of this parameter.
|
390
|
+
not match the value of this parameter. This is useful when
|
391
|
+
[debugging](#debugging), rather than as a general error detection
|
392
|
+
system.
|
557
393
|
|
558
394
|
obj = BinData::String.new(:check_value => lambda { /aaa/ =~ value })
|
559
395
|
obj.read("baaa!") #=> "baaa!"
|
@@ -708,16 +544,16 @@ details.
|
|
708
544
|
|
709
545
|
### Fixed Sized Strings
|
710
546
|
|
711
|
-
Fixed sized strings may have a set length. If an assigned
|
712
|
-
shorter than this length, it will be padded to this length. If
|
713
|
-
length is set, the length is taken to be the length of the assigned
|
547
|
+
Fixed sized strings may have a set length (in bytes). If an assigned
|
548
|
+
value is shorter than this length, it will be padded to this length. If
|
549
|
+
no length is set, the length is taken to be the length of the assigned
|
714
550
|
value.
|
715
551
|
|
716
552
|
There are several parameters that are specific to fixed sized strings.
|
717
553
|
|
718
554
|
`:read_length`
|
719
555
|
|
720
|
-
: The length to use when reading a value.
|
556
|
+
: The length in bytes to use when reading a value.
|
721
557
|
|
722
558
|
obj = BinData::String.new(:read_length => 5)
|
723
559
|
obj.read("abcdefghij")
|
@@ -742,13 +578,13 @@ There are several parameters that are specific to fixed sized strings.
|
|
742
578
|
obj #=> "abcdef"
|
743
579
|
{:ruby}
|
744
580
|
|
745
|
-
`:
|
581
|
+
`:pad_byte`
|
746
582
|
|
747
|
-
: The character to use when padding a string to a
|
748
|
-
values are `Integers` and `Strings` of
|
749
|
-
|
583
|
+
: Defaults to `"\0"`. The character to use when padding a string to a
|
584
|
+
set length. Valid values are `Integers` and `Strings` of one byte.
|
585
|
+
Multi byte padding is not supported.
|
750
586
|
|
751
|
-
obj = BinData::String.new(:length => 6, :
|
587
|
+
obj = BinData::String.new(:length => 6, :pad_byte => 'A')
|
752
588
|
obj.assign("abcd")
|
753
589
|
obj.snapshot #=> "abcdAA"
|
754
590
|
obj.to_binary_s #=> "abcdAA"
|
@@ -757,7 +593,7 @@ There are several parameters that are specific to fixed sized strings.
|
|
757
593
|
`:trim_padding`
|
758
594
|
|
759
595
|
: Boolean, default `false`. If set, the value of this string will
|
760
|
-
have all
|
596
|
+
have all pad_bytes trimmed from the end of the string. The value
|
761
597
|
will not be trimmed when writing.
|
762
598
|
|
763
599
|
obj = BinData::String.new(:length => 6, :trim_value => true)
|
@@ -769,7 +605,7 @@ There are several parameters that are specific to fixed sized strings.
|
|
769
605
|
### Zero Terminated Strings
|
770
606
|
|
771
607
|
These strings are modelled on the C style of string - a sequence of
|
772
|
-
bytes terminated by a null (`"\0"`)
|
608
|
+
bytes terminated by a null (`"\0"`) byte.
|
773
609
|
|
774
610
|
obj = BinData::Stringz.new
|
775
611
|
obj.read("abcd\000efgh")
|
@@ -780,8 +616,8 @@ bytes terminated by a null (`"\0"`) character.
|
|
780
616
|
|
781
617
|
## User Defined Primitive Types
|
782
618
|
|
783
|
-
Most user defined types will be Records
|
784
|
-
create a custom type
|
619
|
+
Most user defined types will be Records but occasionally we'd like to
|
620
|
+
create a custom primitive type.
|
785
621
|
|
786
622
|
Let us revisit the Pascal String example.
|
787
623
|
|
@@ -818,6 +654,58 @@ We create this type of custom string by inheriting from
|
|
818
654
|
end
|
819
655
|
{:ruby}
|
820
656
|
|
657
|
+
A user defined primitive type has both an internal (binary structure)
|
658
|
+
and an external (ruby interface) representation. The internal
|
659
|
+
representation is encapsulated and inaccessible from the external ruby
|
660
|
+
interface.
|
661
|
+
|
662
|
+
Consider a LispBool type that uses `:t` for true and `nil` for false.
|
663
|
+
The binary representation is a signed byte with value `1` for true and
|
664
|
+
`-1` for false.
|
665
|
+
|
666
|
+
class LispBool < BinData::Primitive
|
667
|
+
int8 :val
|
668
|
+
|
669
|
+
def get
|
670
|
+
case self.val
|
671
|
+
when 1
|
672
|
+
:t
|
673
|
+
when -1
|
674
|
+
nil
|
675
|
+
else
|
676
|
+
nil # unknown value, default to false
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
def set(v)
|
681
|
+
case v
|
682
|
+
when :t
|
683
|
+
self.val = 1
|
684
|
+
when nil
|
685
|
+
self.val = -1
|
686
|
+
else
|
687
|
+
self.val = -1 # unknown value, default to false
|
688
|
+
end
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
b = LispBool.new
|
693
|
+
|
694
|
+
b.assign(:t)
|
695
|
+
b.to_binary_s #=> "\001"
|
696
|
+
|
697
|
+
b.read("\xff")
|
698
|
+
b.snapshot #=> nil
|
699
|
+
{:ruby}
|
700
|
+
|
701
|
+
`#read` and `#write` use the internal representation. `#assign` and
|
702
|
+
`#snapshot` use the external representation. Mixing them up will lead
|
703
|
+
to undefined behaviour.
|
704
|
+
|
705
|
+
b = LispBool.new
|
706
|
+
b.assign(1) #=> undefined. Don't do this.
|
707
|
+
{:ruby}
|
708
|
+
|
821
709
|
### Advanced User Defined Primitive Types
|
822
710
|
|
823
711
|
Sometimes a user defined primitive type can not easily be declaratively
|
@@ -896,27 +784,32 @@ Here is an example of a big integer implementation.
|
|
896
784
|
|
897
785
|
---------------------------------------------------------------------------
|
898
786
|
|
899
|
-
#
|
787
|
+
# Compound Types
|
788
|
+
|
789
|
+
Compound types contain more that a single value. These types are
|
790
|
+
Records, Arrays and Choices.
|
791
|
+
|
792
|
+
## Arrays
|
900
793
|
|
901
794
|
A BinData array is a list of data objects of the same type. It behaves
|
902
795
|
much the same as the standard Ruby array, supporting most of the common
|
903
796
|
methods.
|
904
797
|
|
905
|
-
|
798
|
+
### Array syntax
|
906
799
|
|
907
800
|
When instantiating an array, the type of object it contains must be
|
908
801
|
specified. The two different ways of declaring this are the `:type`
|
909
802
|
parameter and the block form.
|
910
803
|
|
911
804
|
class A < BinData::Record
|
912
|
-
|
805
|
+
array :numbers, :type => :uint8, :initial_length => 3
|
913
806
|
end
|
914
807
|
-vs-
|
915
808
|
|
916
809
|
class A < BinData::Record
|
917
|
-
|
918
|
-
|
919
|
-
|
810
|
+
array :numbers, :initial_length => 3 do
|
811
|
+
uint8
|
812
|
+
end
|
920
813
|
end
|
921
814
|
{:ruby}
|
922
815
|
|
@@ -930,15 +823,29 @@ array type has parameters, the block form becomes easier to read.
|
|
930
823
|
-vs-
|
931
824
|
|
932
825
|
class A < BinData::Record
|
933
|
-
|
934
|
-
|
935
|
-
|
826
|
+
array :numbers, :initial_length => 3 do
|
827
|
+
uint8 :initial_value => :index
|
828
|
+
end
|
829
|
+
end
|
830
|
+
{:ruby}
|
831
|
+
|
832
|
+
An array can also be declared as a custom type by moving the contents of
|
833
|
+
the block into a custom class. The above example could alternatively be
|
834
|
+
declared as:
|
835
|
+
|
836
|
+
class NumberArray < BinData::Array
|
837
|
+
uint8 :initial_value => :index
|
838
|
+
end
|
839
|
+
|
840
|
+
class A < BinData::Record
|
841
|
+
number_array :numbers, :initial_length => 3
|
936
842
|
end
|
937
843
|
{:ruby}
|
938
844
|
|
845
|
+
|
939
846
|
If the block form has multiple types declared, they are interpreted as
|
940
|
-
the contents of an anonymous `struct
|
941
|
-
following representation of a polygon.
|
847
|
+
the contents of an [anonymous `struct`](#nested_records). To illustrate
|
848
|
+
this, consider the following representation of a polygon.
|
942
849
|
|
943
850
|
class Polygon < BinData::Record
|
944
851
|
endian :little
|
@@ -956,7 +863,7 @@ following representation of a polygon.
|
|
956
863
|
triangle.points << {:x => 5, :y => 6}
|
957
864
|
{:ruby}
|
958
865
|
|
959
|
-
|
866
|
+
### Array parameters
|
960
867
|
|
961
868
|
There are two different parameters that specify the length of the array.
|
962
869
|
|
@@ -999,9 +906,7 @@ There are two different parameters that specify the length of the array.
|
|
999
906
|
obj.snapshot #=> [2, 3, 4, 5, 6, 7]
|
1000
907
|
{:ruby}
|
1001
908
|
|
1002
|
-
|
1003
|
-
|
1004
|
-
# Choices
|
909
|
+
## Choices
|
1005
910
|
|
1006
911
|
A Choice is a collection of data objects of which only one is active at
|
1007
912
|
any particular time. Method calls will be delegated to the active
|
@@ -1009,7 +914,7 @@ choice. The possible types of objects that a choice contains is
|
|
1009
914
|
controlled by the `:choices` parameter, while the `:selection` parameter
|
1010
915
|
specifies the active choice.
|
1011
916
|
|
1012
|
-
|
917
|
+
### Choice syntax
|
1013
918
|
|
1014
919
|
Choices have two ways of specifying the possible data objects they can
|
1015
920
|
contain. The `:choices` parameter or the block form. The block form is
|
@@ -1031,6 +936,20 @@ usually clearer and is prefered.
|
|
1031
936
|
end
|
1032
937
|
{:ruby}
|
1033
938
|
|
939
|
+
Like all compound types, a choice can be declared as its own type. The
|
940
|
+
above example can be declared as:
|
941
|
+
|
942
|
+
class BigLittleInt16 < BinData::Choice
|
943
|
+
int16be 0
|
944
|
+
int16le 1
|
945
|
+
end
|
946
|
+
|
947
|
+
class MyInt16 < BinData::Record
|
948
|
+
uint8 :e, :check_value => lambda { value == 0 or value == 1 }
|
949
|
+
bit_little_int_16 :int, :selection => :e
|
950
|
+
end
|
951
|
+
{:ruby}
|
952
|
+
|
1034
953
|
The general form of the choice is
|
1035
954
|
|
1036
955
|
class MyRecord < BinData::Record
|
@@ -1050,7 +969,7 @@ The general form of the choice is
|
|
1050
969
|
choice is currently active. The key can be any ruby type (`String`,
|
1051
970
|
`Numeric` etc) except `Symbol`.
|
1052
971
|
|
1053
|
-
|
972
|
+
### Choice parameters
|
1054
973
|
|
1055
974
|
`:choices`
|
1056
975
|
|
@@ -1103,7 +1022,7 @@ Examples
|
|
1103
1022
|
obj.to_binary_s #=> "\000\005\000\000\000"
|
1104
1023
|
{:ruby}
|
1105
1024
|
|
1106
|
-
|
1025
|
+
### Default selection
|
1107
1026
|
|
1108
1027
|
A key of `:default` can be specified as a default selection. If the value of the
|
1109
1028
|
selection isn't specified then the :default will be used. The previous `MyNumber`
|
@@ -1122,6 +1041,147 @@ is big endian. This can be concisely written as:
|
|
1122
1041
|
|
1123
1042
|
---------------------------------------------------------------------------
|
1124
1043
|
|
1044
|
+
# Common Operations
|
1045
|
+
|
1046
|
+
There are operations common to all BinData types, including user defined
|
1047
|
+
ones. These are summarised here.
|
1048
|
+
|
1049
|
+
## Reading and writing
|
1050
|
+
|
1051
|
+
`::read(io)`
|
1052
|
+
|
1053
|
+
: Creates a BinData object and reads its value from the given string
|
1054
|
+
or `IO`. The newly created object is returned.
|
1055
|
+
|
1056
|
+
obj = BinData::Int8.read("\xff")
|
1057
|
+
obj.snapshot #=> -1
|
1058
|
+
{:ruby}
|
1059
|
+
|
1060
|
+
`#read(io)`
|
1061
|
+
|
1062
|
+
: Reads and assigns binary data read from `io`.
|
1063
|
+
|
1064
|
+
obj = BinData::Stringz.new
|
1065
|
+
obj.read("string 1\0string 2\0")
|
1066
|
+
obj #=> "string 1"
|
1067
|
+
{:ruby}
|
1068
|
+
|
1069
|
+
`#write(io)`
|
1070
|
+
|
1071
|
+
: Writes the binary data representation of the object to `io`.
|
1072
|
+
|
1073
|
+
File.open("...", "wb") do |io|
|
1074
|
+
obj = BinData::Uint64be.new(568290145640170)
|
1075
|
+
obj.write(io)
|
1076
|
+
end
|
1077
|
+
{:ruby}
|
1078
|
+
|
1079
|
+
`#to_binary_s`
|
1080
|
+
|
1081
|
+
: Returns the binary data representation of this object as a string.
|
1082
|
+
|
1083
|
+
obj = BinData::Uint16be.new(4660)
|
1084
|
+
obj.to_binary_s #=> "\022\064"
|
1085
|
+
{:ruby}
|
1086
|
+
|
1087
|
+
## Manipulating
|
1088
|
+
|
1089
|
+
`#assign(value)`
|
1090
|
+
|
1091
|
+
: Assigns the given value to this object. `value` can be of the same
|
1092
|
+
format as produced by `#snapshot`, or it can be a compatible data
|
1093
|
+
object.
|
1094
|
+
|
1095
|
+
arr = BinData::Array.new(:type => :uint8)
|
1096
|
+
arr.assign([1, 2, 3, 4])
|
1097
|
+
arr.snapshot #=> [1, 2, 3, 4]
|
1098
|
+
{:ruby}
|
1099
|
+
|
1100
|
+
`#clear`
|
1101
|
+
|
1102
|
+
: Resets this object to its initial state.
|
1103
|
+
|
1104
|
+
obj = BinData::Int32be.new(:initial_value => 42)
|
1105
|
+
obj.assign(50)
|
1106
|
+
obj.clear
|
1107
|
+
obj #=> 42
|
1108
|
+
{:ruby}
|
1109
|
+
|
1110
|
+
`#clear?`
|
1111
|
+
|
1112
|
+
: Returns whether this object is in its initial state.
|
1113
|
+
|
1114
|
+
arr = BinData::Array.new(:type => :uint16be, :initial_length => 5)
|
1115
|
+
arr[3] = 42
|
1116
|
+
arr.clear? #=> false
|
1117
|
+
|
1118
|
+
arr[3].clear
|
1119
|
+
arr.clear? #=> true
|
1120
|
+
{:ruby}
|
1121
|
+
|
1122
|
+
## Inspecting
|
1123
|
+
|
1124
|
+
`#num_bytes`
|
1125
|
+
|
1126
|
+
: Returns the number of bytes required for the binary data
|
1127
|
+
representation of this object.
|
1128
|
+
|
1129
|
+
arr = BinData::Array.new(:type => :uint16be, :initial_length => 5)
|
1130
|
+
arr[0].num_bytes #=> 2
|
1131
|
+
arr.num_bytes #=> 10
|
1132
|
+
{:ruby}
|
1133
|
+
|
1134
|
+
`#snapshot`
|
1135
|
+
|
1136
|
+
: Returns the value of this object as primitive Ruby objects
|
1137
|
+
(numerics, strings, arrays and hashs). The output of `#snapshot`
|
1138
|
+
may be useful for serialization or as a reduced memory usage
|
1139
|
+
representation.
|
1140
|
+
|
1141
|
+
obj = BinData::Uint8.new(2)
|
1142
|
+
obj.class #=> BinData::Uint8
|
1143
|
+
obj + 3 #=> 5
|
1144
|
+
|
1145
|
+
obj.snapshot #=> 2
|
1146
|
+
obj.snapshot.class #=> Fixnum
|
1147
|
+
{:ruby}
|
1148
|
+
|
1149
|
+
`#offset`
|
1150
|
+
|
1151
|
+
: Returns the offset of this object with respect to the most distant
|
1152
|
+
ancestor structure it is contained within. This is most likely to
|
1153
|
+
be used with arrays and records.
|
1154
|
+
|
1155
|
+
class Tuple < BinData::Record
|
1156
|
+
int8 :a
|
1157
|
+
int8 :b
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
arr = BinData::Array.new(:type => :tuple, :initial_length => 3)
|
1161
|
+
arr[2].b.offset #=> 5
|
1162
|
+
{:ruby}
|
1163
|
+
|
1164
|
+
`#rel_offset`
|
1165
|
+
|
1166
|
+
: Returns the offset of this object with respect to the parent
|
1167
|
+
structure it is contained within. Compare this to `#offset`.
|
1168
|
+
|
1169
|
+
class Tuple < BinData::Record
|
1170
|
+
int8 :a
|
1171
|
+
int8 :b
|
1172
|
+
end
|
1173
|
+
|
1174
|
+
arr = BinData::Array.new(:type => :tuple, :initial_length => 3)
|
1175
|
+
arr[2].b.rel_offset #=> 1
|
1176
|
+
{:ruby}
|
1177
|
+
|
1178
|
+
`#inspect`
|
1179
|
+
|
1180
|
+
: Returns a human readable representation of this object. This is a
|
1181
|
+
shortcut to #snapshot.inspect.
|
1182
|
+
|
1183
|
+
---------------------------------------------------------------------------
|
1184
|
+
|
1125
1185
|
# Advanced Topics
|
1126
1186
|
|
1127
1187
|
## Debugging
|
@@ -1207,19 +1267,19 @@ Mandatory parameters must be specified when creating an instance of the
|
|
1207
1267
|
type.
|
1208
1268
|
|
1209
1269
|
class Polygon < BinData::Record
|
1210
|
-
mandatory_parameter :
|
1270
|
+
mandatory_parameter :num_vertices
|
1211
1271
|
|
1212
1272
|
uint8 :num, :value => lambda { vertices.length }
|
1213
|
-
array :vertices, :initial_length => :
|
1273
|
+
array :vertices, :initial_length => :num_vertices do
|
1214
1274
|
int8 :x
|
1215
1275
|
int8 :y
|
1216
1276
|
end
|
1217
1277
|
end
|
1218
1278
|
|
1219
1279
|
triangle = Polygon.new
|
1220
|
-
#=> raises ArgumentError: parameter '
|
1280
|
+
#=> raises ArgumentError: parameter 'num_vertices' must be specified in Polygon
|
1221
1281
|
|
1222
|
-
triangle = Polygon.new(:
|
1282
|
+
triangle = Polygon.new(:num_vertices => 3)
|
1223
1283
|
triangle.snapshot #=> {"num" => 3, "vertices" =>
|
1224
1284
|
[{"x"=>0, "y"=>0}, {"x"=>0, "y"=>0}, {"x"=>0, "y"=>0}]}
|
1225
1285
|
{:ruby}
|
@@ -1337,14 +1397,44 @@ do not stream well as they must be buffered by the client before being
|
|
1337
1397
|
processed. Consider using an explicit length when creating a new file format
|
1338
1398
|
as it is easier to work with.
|
1339
1399
|
|
1340
|
-
##
|
1400
|
+
## Advanced Bitfields
|
1401
|
+
|
1402
|
+
Most types in a record are byte oriented. [Bitfields](#bit_based_integers)
|
1403
|
+
allow access to individual bits in an octet stream.
|
1404
|
+
|
1405
|
+
Sometimes a bitfield has unused elements such as
|
1406
|
+
|
1407
|
+
class RecordWithBitfield < BinData::Record
|
1408
|
+
bit1 :foo
|
1409
|
+
bit1 :bar
|
1410
|
+
bit1 :baz
|
1411
|
+
bit5 :unused
|
1412
|
+
|
1413
|
+
stringz :qux
|
1414
|
+
end
|
1415
|
+
{:ruby}
|
1416
|
+
|
1417
|
+
The problem with specifying an unused field is that the size of this
|
1418
|
+
field must be manually counted. This is a potential source of errors.
|
1341
1419
|
|
1342
|
-
|
1343
|
-
|
1344
|
-
come across a format where primitive types (string and numerics) are not
|
1345
|
-
aligned on byte boundaries.
|
1420
|
+
BinData provides a shortcut to skip to the next byte boundary with the
|
1421
|
+
`resume_byte_alignment` keyword.
|
1346
1422
|
|
1347
|
-
class
|
1423
|
+
class RecordWithBitfield < BinData::Record
|
1424
|
+
bit1 :foo
|
1425
|
+
bit1 :bar
|
1426
|
+
bit1 :baz
|
1427
|
+
resume_byte_alignment
|
1428
|
+
|
1429
|
+
stringz :qux
|
1430
|
+
end
|
1431
|
+
{:ruby}
|
1432
|
+
|
1433
|
+
Occasionally you will come across a format where primitive types (string
|
1434
|
+
and numerics) are not aligned on byte boundaries but are to be packed in
|
1435
|
+
the bit stream.
|
1436
|
+
|
1437
|
+
class PackedRecord < BinData::Record
|
1348
1438
|
bit4 :a
|
1349
1439
|
string :b, :length => 2 # note: byte-aligned
|
1350
1440
|
bit1 :c
|
@@ -1352,8 +1442,8 @@ aligned on byte boundaries.
|
|
1352
1442
|
bit3 :e
|
1353
1443
|
end
|
1354
1444
|
|
1355
|
-
|
1356
|
-
|
1445
|
+
obj = PackedRecord.read("\xff" * 10)
|
1446
|
+
obj.to_binary_s #=> "\360\377\377\200\377\377\340"
|
1357
1447
|
{:ruby}
|
1358
1448
|
|
1359
1449
|
The above declaration does not work as expected because BinData's
|
@@ -1368,7 +1458,7 @@ versions of `string` and `int16le`.
|
|
1368
1458
|
bit_aligned
|
1369
1459
|
end
|
1370
1460
|
|
1371
|
-
class
|
1461
|
+
class PackedRecord < BinData::Record
|
1372
1462
|
bit4 :a
|
1373
1463
|
bit_string :b, :length => 2
|
1374
1464
|
bit1 :c
|
@@ -1376,8 +1466,8 @@ versions of `string` and `int16le`.
|
|
1376
1466
|
bit3 :e
|
1377
1467
|
end
|
1378
1468
|
|
1379
|
-
|
1380
|
-
|
1469
|
+
obj = PackedRecord.read("\xff" * 10)
|
1470
|
+
obj.to_binary_s #=> "\377\377\377\377\377"
|
1381
1471
|
{:ruby}
|
1382
1472
|
|
1383
1473
|
---------------------------------------------------------------------------
|
@@ -1465,6 +1555,9 @@ lists](http://bindata.rubyforge.org/svn/trunk/examples/list.rb).
|
|
1465
1555
|
|
1466
1556
|
# Alternatives
|
1467
1557
|
|
1558
|
+
This section is purely historic. All the alternatives to BinData are
|
1559
|
+
no longer actively maintained.
|
1560
|
+
|
1468
1561
|
There are several alternatives to BinData. Below is a comparison
|
1469
1562
|
between BinData and its alternatives.
|
1470
1563
|
|