bindata 1.2.2 → 1.3.1
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.
Potentially problematic release.
This version of bindata might be problematic. Click here for more details.
- data/ChangeLog +12 -0
- data/NEWS +53 -0
- data/Rakefile +2 -1
- data/examples/NBT.txt +149 -0
- data/examples/ip_address.rb +1 -2
- data/examples/list.rb +124 -0
- data/examples/nbt.rb +195 -0
- data/lib/bindata.rb +4 -3
- data/lib/bindata/alignment.rb +86 -0
- data/lib/bindata/array.rb +21 -29
- data/lib/bindata/base.rb +82 -81
- data/lib/bindata/base_primitive.rb +66 -48
- data/lib/bindata/choice.rb +18 -28
- data/lib/bindata/deprecated.rb +17 -0
- data/lib/bindata/dsl.rb +25 -15
- data/lib/bindata/int.rb +2 -2
- data/lib/bindata/io.rb +8 -6
- data/lib/bindata/offset.rb +91 -0
- data/lib/bindata/primitive.rb +22 -11
- data/lib/bindata/record.rb +40 -10
- data/lib/bindata/sanitize.rb +15 -30
- data/lib/bindata/string.rb +16 -17
- data/lib/bindata/stringz.rb +0 -1
- data/lib/bindata/struct.rb +17 -6
- data/lib/bindata/trace.rb +52 -0
- data/lib/bindata/wrapper.rb +28 -6
- data/manual.haml +56 -10
- data/manual.md +318 -113
- data/spec/alignment_spec.rb +61 -0
- data/spec/array_spec.rb +139 -178
- data/spec/base_primitive_spec.rb +86 -111
- data/spec/base_spec.rb +200 -172
- data/spec/bits_spec.rb +45 -53
- data/spec/choice_spec.rb +91 -87
- data/spec/deprecated_spec.rb +36 -14
- data/spec/float_spec.rb +16 -68
- data/spec/int_spec.rb +26 -27
- data/spec/io_spec.rb +105 -105
- data/spec/lazy_spec.rb +50 -50
- data/spec/primitive_spec.rb +36 -36
- data/spec/record_spec.rb +134 -134
- data/spec/registry_spec.rb +34 -38
- data/spec/rest_spec.rb +8 -11
- data/spec/skip_spec.rb +9 -17
- data/spec/spec_common.rb +4 -0
- data/spec/string_spec.rb +92 -115
- data/spec/stringz_spec.rb +41 -74
- data/spec/struct_spec.rb +132 -153
- data/spec/system_spec.rb +115 -60
- data/spec/wrapper_spec.rb +63 -31
- data/tasks/pkg.rake +1 -1
- metadata +15 -7
data/manual.md
CHANGED
@@ -2,7 +2,7 @@ Title: BinData Reference Manual
|
|
2
2
|
|
3
3
|
{:ruby: lang=ruby html_use_syntax=true}
|
4
4
|
|
5
|
-
# BinData
|
5
|
+
# BinData - Parsing Binary Data in Ruby
|
6
6
|
|
7
7
|
A declarative way to read and write structured binary data.
|
8
8
|
|
@@ -37,14 +37,18 @@ There is a better way.
|
|
37
37
|
BinData makes it easy to specify the structure of the data you are
|
38
38
|
manipulating.
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
It supports all the common datatypes that are found in structured binary
|
41
|
+
data. Support for dependent and variable length fields is built in.
|
42
42
|
|
43
43
|
## License
|
44
44
|
|
45
45
|
BinData is released under the same license as Ruby.
|
46
46
|
|
47
|
-
Copyright © 2007 -
|
47
|
+
Copyright © 2007 - 2011 [Dion Mendel](mailto:dion@lostrealm.com)
|
48
|
+
|
49
|
+
## Donate
|
50
|
+
|
51
|
+
Want to donate? My favourite local charity is [Perth Raptor Care](http://care.raptor.id.au/help.html#PAL).
|
48
52
|
|
49
53
|
---------------------------------------------------------------------------
|
50
54
|
|
@@ -66,17 +70,22 @@ BinData declarations are easy to read. Here's an example.
|
|
66
70
|
|
67
71
|
class MyFancyFormat < BinData::Record
|
68
72
|
stringz :comment
|
69
|
-
uint8 :
|
73
|
+
uint8 :num, :check_value => lambda { value.even? }
|
70
74
|
array :some_ints, :type => :int32be, :initial_length => :num_ints
|
71
75
|
end
|
72
76
|
{:ruby}
|
73
77
|
|
74
78
|
This fancy format describes the following collection of data:
|
75
79
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
+
`:comment`
|
81
|
+
: A zero terminated string
|
82
|
+
|
83
|
+
`:num`
|
84
|
+
: An unsigned 8bit integer which must by even
|
85
|
+
|
86
|
+
`:some_ints`
|
87
|
+
: A sequence of unsigned 32bit big endian integers. The number of
|
88
|
+
integers is given by the value of `:num`
|
80
89
|
|
81
90
|
The BinData declaration matches the English description closely.
|
82
91
|
Compare the above declaration with the equivalent `#unpack` code to read
|
@@ -96,7 +105,7 @@ The BinData declaration clearly shows the structure of the record. The
|
|
96
105
|
The general usage of BinData is to declare a structured collection of
|
97
106
|
data as a user defined record. This record can be instantiated, read,
|
98
107
|
written and manipulated without the user having to be concerned with the
|
99
|
-
underlying binary representation
|
108
|
+
underlying binary data representation.
|
100
109
|
|
101
110
|
---------------------------------------------------------------------------
|
102
111
|
|
@@ -112,36 +121,34 @@ ones. These are summarised here.
|
|
112
121
|
: Creates a BinData object and reads its value from the given string
|
113
122
|
or `IO`. The newly created object is returned.
|
114
123
|
|
115
|
-
|
116
|
-
|
124
|
+
obj = BinData::Int8.read("\xff")
|
125
|
+
obj.snapshot #=> -1
|
117
126
|
{:ruby}
|
118
127
|
|
119
128
|
`#read(io)`
|
120
129
|
|
121
130
|
: Reads and assigns binary data read from `io`.
|
122
131
|
|
123
|
-
obj = BinData::
|
124
|
-
obj.read("\
|
125
|
-
obj
|
132
|
+
obj = BinData::Stringz.new
|
133
|
+
obj.read("string 1\0string 2\0")
|
134
|
+
obj #=> "string 1"
|
126
135
|
{:ruby}
|
127
136
|
|
128
137
|
`#write(io)`
|
129
138
|
|
130
|
-
: Writes the binary representation of the object to `io`.
|
139
|
+
: Writes the binary data representation of the object to `io`.
|
131
140
|
|
132
141
|
File.open("...", "wb") do |io|
|
133
|
-
obj = BinData::Uint64be.new
|
134
|
-
obj.value = 568290145640170
|
142
|
+
obj = BinData::Uint64be.new(568290145640170)
|
135
143
|
obj.write(io)
|
136
144
|
end
|
137
145
|
{:ruby}
|
138
146
|
|
139
147
|
`#to_binary_s`
|
140
148
|
|
141
|
-
: Returns the binary representation of this object as a string.
|
149
|
+
: Returns the binary data representation of this object as a string.
|
142
150
|
|
143
|
-
obj = BinData::Uint16be.new
|
144
|
-
obj.assign(4660)
|
151
|
+
obj = BinData::Uint16be.new(4660)
|
145
152
|
obj.to_binary_s #=> "\022\064"
|
146
153
|
{:ruby}
|
147
154
|
|
@@ -165,7 +172,7 @@ ones. These are summarised here.
|
|
165
172
|
obj = BinData::Int32be.new(:initial_value => 42)
|
166
173
|
obj.assign(50)
|
167
174
|
obj.clear
|
168
|
-
obj
|
175
|
+
obj #=> 42
|
169
176
|
{:ruby}
|
170
177
|
|
171
178
|
`#clear?`
|
@@ -184,8 +191,8 @@ ones. These are summarised here.
|
|
184
191
|
|
185
192
|
`#num_bytes`
|
186
193
|
|
187
|
-
: Returns the number of bytes required for the binary
|
188
|
-
of this object.
|
194
|
+
: Returns the number of bytes required for the binary data
|
195
|
+
representation of this object.
|
189
196
|
|
190
197
|
arr = BinData::Array.new(:type => :uint16be, :initial_length => 5)
|
191
198
|
arr[0].num_bytes #=> 2
|
@@ -199,11 +206,11 @@ ones. These are summarised here.
|
|
199
206
|
may be useful for serialization or as a reduced memory usage
|
200
207
|
representation.
|
201
208
|
|
202
|
-
obj = BinData::Uint8.new
|
203
|
-
obj.
|
204
|
-
obj + 3 #=>
|
209
|
+
obj = BinData::Uint8.new(2)
|
210
|
+
obj.class #=> BinData::Uint8
|
211
|
+
obj + 3 #=> 5
|
205
212
|
|
206
|
-
obj.snapshot #=>
|
213
|
+
obj.snapshot #=> 2
|
207
214
|
obj.snapshot.class #=> Fixnum
|
208
215
|
{:ruby}
|
209
216
|
|
@@ -260,10 +267,10 @@ one or more fields.
|
|
260
267
|
converted from `CamelCase` to lowercased `underscore_style`.
|
261
268
|
|
262
269
|
`field_name`
|
263
|
-
: is the name by which you can access the field. Use
|
264
|
-
|
265
|
-
|
266
|
-
|
270
|
+
: is the name by which you can access the field. Use a `Symbol` for
|
271
|
+
the name. If the name is omitted, then this particular field
|
272
|
+
is anonymous. An anonymous field is still read and written, but
|
273
|
+
will not appear in `#snapshot`.
|
267
274
|
|
268
275
|
Each field may have optional *parameters* for how to process the data.
|
269
276
|
The parameters are passed as a `Hash` with `Symbols` for keys.
|
@@ -273,9 +280,8 @@ This means that any parameter value must not have side effects.
|
|
273
280
|
Here are some examples of legal values for parameters.
|
274
281
|
|
275
282
|
* `:param => 5`
|
276
|
-
* `:param => lambda { 5 + 2 }`
|
277
283
|
* `:param => lambda { foo + 2 }`
|
278
|
-
* `:param => :
|
284
|
+
* `:param => :bar`
|
279
285
|
|
280
286
|
The simplest case is when the value is a literal value, such as `5`.
|
281
287
|
|
@@ -285,14 +291,14 @@ parent is an instance of `MyName`.
|
|
285
291
|
|
286
292
|
If the value is a symbol, it is taken as syntactic sugar for a lambda
|
287
293
|
containing the value of the symbol.
|
288
|
-
e.g `:param => :
|
294
|
+
e.g `:param => :bar` is `:param => lambda { bar }`
|
289
295
|
|
290
296
|
## Specifying default endian
|
291
297
|
|
292
298
|
The endianess of numeric types must be explicitly defined so that the
|
293
299
|
code produced is independent of architecture. However, explicitly
|
294
300
|
specifying the endian for each numeric field can result in a bloated
|
295
|
-
declaration that
|
301
|
+
declaration that is difficult to read.
|
296
302
|
|
297
303
|
class A < BinData::Record
|
298
304
|
int16be :a
|
@@ -324,27 +330,7 @@ The increase in clarity can be seen with the above example. The
|
|
324
330
|
`endian` keyword will cascade to nested types, as illustrated with the
|
325
331
|
array in the above example.
|
326
332
|
|
327
|
-
##
|
328
|
-
|
329
|
-
A record may contain optional fields. The optional state of a field is
|
330
|
-
decided by the `:onlyif` parameter. If the value of this parameter is
|
331
|
-
`false`, then the field will be as if it didn't exist in the record.
|
332
|
-
|
333
|
-
class RecordWithOptionalField < BinData::Record
|
334
|
-
...
|
335
|
-
uint8 :comment_flag
|
336
|
-
string :comment, :length => 20, :onlyif => :has_comment?
|
337
|
-
|
338
|
-
def has_comment?
|
339
|
-
comment_flag.nonzero?
|
340
|
-
end
|
341
|
-
end
|
342
|
-
{:ruby}
|
343
|
-
|
344
|
-
In the above example, the `comment` field is only included in the record
|
345
|
-
if the value of the `comment_flag` field is non zero.
|
346
|
-
|
347
|
-
## Handling dependencies between fields
|
333
|
+
## Dependencies between fields
|
348
334
|
|
349
335
|
A common occurence in binary file formats is one field depending upon
|
350
336
|
the value of another. e.g. A string preceded by its length.
|
@@ -417,6 +403,96 @@ It is important to note with dependencies, that a field can only depend
|
|
417
403
|
on one before it. You can't have a string which has the characters
|
418
404
|
first and the length afterwards.
|
419
405
|
|
406
|
+
## Nested Records
|
407
|
+
|
408
|
+
Records can be used as a mean of grouping together related data. Rather
|
409
|
+
than declaring a single flat record, a nested structure can be used to
|
410
|
+
document this grouping.
|
411
|
+
|
412
|
+
class Coord < BinData::Record
|
413
|
+
endian :little
|
414
|
+
double :x
|
415
|
+
double :z
|
416
|
+
double :y
|
417
|
+
end
|
418
|
+
|
419
|
+
class LabeledCoord < BinData::Record
|
420
|
+
string :label, :length => 20
|
421
|
+
coord :coord
|
422
|
+
end
|
423
|
+
|
424
|
+
pos = LabeledCoord.new(:label => "red leader")
|
425
|
+
pos.coord.assign(:x => 2.0, :y => 0, :z => -1.57)
|
426
|
+
{:ruby}
|
427
|
+
|
428
|
+
BinData supports anonymous nested records. The previous example could
|
429
|
+
also be declared using the `struct` keyword as follows:
|
430
|
+
|
431
|
+
class LabeledCoord < BinData::Record
|
432
|
+
string :label, :length => 20
|
433
|
+
|
434
|
+
struct :coord do
|
435
|
+
endian :little
|
436
|
+
double :x
|
437
|
+
double :z
|
438
|
+
double :y
|
439
|
+
end
|
440
|
+
end
|
441
|
+
{:ruby}
|
442
|
+
|
443
|
+
## Bitfields
|
444
|
+
|
445
|
+
Most types in a record are byte oriented. Bitfields allow us to access
|
446
|
+
individual bits in an octet stream.
|
447
|
+
|
448
|
+
Sometimes a bitfield has unused elements such as
|
449
|
+
|
450
|
+
class RecordWithBitfield < BinData::Record
|
451
|
+
bit1 :foo
|
452
|
+
bit1 :bar
|
453
|
+
bit1 :baz
|
454
|
+
bit5 :unused
|
455
|
+
|
456
|
+
stringz :qux
|
457
|
+
end
|
458
|
+
{:ruby}
|
459
|
+
|
460
|
+
The problem with specifying an unused field is that the size of this
|
461
|
+
field must be manually counted. This is a potential source of errors.
|
462
|
+
|
463
|
+
BinData provides a shortcut to skip to the next byte boundary with the
|
464
|
+
`resume_byte_alignment` keyword.
|
465
|
+
|
466
|
+
class RecordWithBitfield < BinData::Record
|
467
|
+
bit1 :foo
|
468
|
+
bit1 :bar
|
469
|
+
bit1 :baz
|
470
|
+
resume_byte_alignment
|
471
|
+
|
472
|
+
stringz :qux
|
473
|
+
end
|
474
|
+
{:ruby}
|
475
|
+
|
476
|
+
## Optional fields
|
477
|
+
|
478
|
+
A record may contain optional fields. The optional state of a field is
|
479
|
+
decided by the `:onlyif` parameter. If the value of this parameter is
|
480
|
+
`false`, then the field will be as if it didn't exist in the record.
|
481
|
+
|
482
|
+
class RecordWithOptionalField < BinData::Record
|
483
|
+
...
|
484
|
+
uint8 :comment_flag
|
485
|
+
string :comment, :length => 20, :onlyif => :has_comment?
|
486
|
+
|
487
|
+
def has_comment?
|
488
|
+
comment_flag.nonzero?
|
489
|
+
end
|
490
|
+
end
|
491
|
+
{:ruby}
|
492
|
+
|
493
|
+
In the above example, the `comment` field is only included in the record
|
494
|
+
if the value of the `comment_flag` field is non zero.
|
495
|
+
|
420
496
|
---------------------------------------------------------------------------
|
421
497
|
|
422
498
|
# Primitive Types
|
@@ -438,8 +514,7 @@ with them as part of a record.
|
|
438
514
|
|
439
515
|
Examples of individual usage:
|
440
516
|
|
441
|
-
int16 = BinData::Int16be.new
|
442
|
-
int16.value = 941
|
517
|
+
int16 = BinData::Int16be.new(941)
|
443
518
|
int16.to_binary_s #=> "\003\255"
|
444
519
|
|
445
520
|
fl = BinData::FloatBe.read("\100\055\370\124") #=> 2.71828174591064
|
@@ -524,7 +599,7 @@ stored as an array of 12bit little endian integers).
|
|
524
599
|
An array of bit based integers will be packed according to their endian.
|
525
600
|
|
526
601
|
In a record, adjacent bitfields will be packed according to their
|
527
|
-
endian. All other fields are byte
|
602
|
+
endian. All other fields are byte-aligned.
|
528
603
|
|
529
604
|
Examples of bit based integers are:
|
530
605
|
|
@@ -642,7 +717,7 @@ There are several parameters that are specific to fixed sized strings.
|
|
642
717
|
|
643
718
|
obj = BinData::String.new(:read_length => 5)
|
644
719
|
obj.read("abcdefghij")
|
645
|
-
obj
|
720
|
+
obj #=> "abcde"
|
646
721
|
{:ruby}
|
647
722
|
|
648
723
|
`:length`
|
@@ -652,15 +727,15 @@ There are several parameters that are specific to fixed sized strings.
|
|
652
727
|
|
653
728
|
obj = BinData::String.new(:length => 6)
|
654
729
|
obj.read("abcdefghij")
|
655
|
-
obj
|
730
|
+
obj #=> "abcdef"
|
656
731
|
|
657
732
|
obj = BinData::String.new(:length => 6)
|
658
|
-
obj.
|
659
|
-
obj
|
733
|
+
obj.assign("abcd")
|
734
|
+
obj #=> "abcd\000\000"
|
660
735
|
|
661
736
|
obj = BinData::String.new(:length => 6)
|
662
|
-
obj.
|
663
|
-
obj
|
737
|
+
obj.assign("abcdefghij")
|
738
|
+
obj #=> "abcdef"
|
664
739
|
{:ruby}
|
665
740
|
|
666
741
|
`:pad_char`
|
@@ -670,8 +745,8 @@ There are several parameters that are specific to fixed sized strings.
|
|
670
745
|
`"\0"` is the default.
|
671
746
|
|
672
747
|
obj = BinData::String.new(:length => 6, :pad_char => 'A')
|
673
|
-
obj.
|
674
|
-
obj.
|
748
|
+
obj.assign("abcd")
|
749
|
+
obj.snapshot #=> "abcdAA"
|
675
750
|
obj.to_binary_s #=> "abcdAA"
|
676
751
|
{:ruby}
|
677
752
|
|
@@ -682,8 +757,8 @@ There are several parameters that are specific to fixed sized strings.
|
|
682
757
|
will not be trimmed when writing.
|
683
758
|
|
684
759
|
obj = BinData::String.new(:length => 6, :trim_value => true)
|
685
|
-
obj.
|
686
|
-
obj.
|
760
|
+
obj.assign("abcd")
|
761
|
+
obj.snapshot #=> "abcd"
|
687
762
|
obj.to_binary_s #=> "abcd\000\000"
|
688
763
|
{:ruby}
|
689
764
|
|
@@ -694,7 +769,7 @@ bytes terminated by a null (`"\0"`) character.
|
|
694
769
|
|
695
770
|
obj = BinData::Stringz.new
|
696
771
|
obj.read("abcd\000efgh")
|
697
|
-
obj
|
772
|
+
obj #=> "abcd"
|
698
773
|
obj.num_bytes #=> 5
|
699
774
|
obj.to_binary_s #=> "abcd\000"
|
700
775
|
{:ruby}
|
@@ -745,9 +820,19 @@ Sometimes a user defined primitive type can not easily be declaratively
|
|
745
820
|
defined. In this case you should inherit from `BinData::BasePrimitive`
|
746
821
|
and implement the following three methods:
|
747
822
|
|
748
|
-
|
749
|
-
|
750
|
-
|
823
|
+
`def value_to_binary_string(value)`
|
824
|
+
|
825
|
+
: Takes a ruby value (`String`, `Numeric` etc) and converts it to
|
826
|
+
the appropriate binary string representation.
|
827
|
+
|
828
|
+
`def read_and_return_value(io)`
|
829
|
+
|
830
|
+
: Reads a number of bytes from `io` and returns a ruby object that
|
831
|
+
represents these bytes.
|
832
|
+
|
833
|
+
`def sensible_default()`
|
834
|
+
|
835
|
+
: The ruby value that a clear object should return.
|
751
836
|
|
752
837
|
Here is an example of a big integer implementation.
|
753
838
|
|
@@ -811,21 +896,62 @@ A BinData array is a list of data objects of the same type. It behaves
|
|
811
896
|
much the same as the standard Ruby array, supporting most of the common
|
812
897
|
methods.
|
813
898
|
|
899
|
+
## Array syntax
|
900
|
+
|
814
901
|
When instantiating an array, the type of object it contains must be
|
815
|
-
specified.
|
902
|
+
specified. The two different ways of declaring this are the `:type`
|
903
|
+
parameter and the block form.
|
904
|
+
|
905
|
+
class A < BinData::Record
|
906
|
+
array :numbers, :type => :uint8, :initial_length => 3
|
907
|
+
end
|
908
|
+
-vs-
|
816
909
|
|
817
|
-
|
818
|
-
|
819
|
-
|
910
|
+
class A < BinData::Record
|
911
|
+
array :numbers, :initial_length => 3 do
|
912
|
+
uint8
|
913
|
+
end
|
914
|
+
end
|
820
915
|
{:ruby}
|
821
916
|
|
822
|
-
|
917
|
+
For the simple case, the `:type` parameter is usually clearer. When the
|
918
|
+
array type has parameters, the block form becomes easier to read.
|
823
919
|
|
824
|
-
|
825
|
-
|
826
|
-
|
920
|
+
class A < BinData::Record
|
921
|
+
array :numbers, :type => [:uint8, {:initial_value => :index}],
|
922
|
+
:initial_length => 3
|
923
|
+
end
|
924
|
+
-vs-
|
925
|
+
|
926
|
+
class A < BinData::Record
|
927
|
+
array :numbers, :initial_length => 3 do
|
928
|
+
uint8 :initial_value => :index
|
929
|
+
end
|
930
|
+
end
|
931
|
+
{:ruby}
|
932
|
+
|
933
|
+
If the block form has multiple types declared, they are interpreted as
|
934
|
+
the contents of an anonymous `struct`. To illustrate this, consider the
|
935
|
+
following representation of a polygon.
|
936
|
+
|
937
|
+
class Polygon < BinData::Record
|
938
|
+
endian :little
|
939
|
+
uint8 :num_points, :value => lambda { points.length }
|
940
|
+
array :points, :initial_length => :num_points do
|
941
|
+
double :x
|
942
|
+
double :y
|
943
|
+
end
|
944
|
+
end
|
945
|
+
|
946
|
+
triangle = Polygon.new
|
947
|
+
triangle.points[0].assign(:x => 1, :y => 2)
|
948
|
+
triangle.points[1].x = 3
|
949
|
+
triangle.points[1].y = 4
|
950
|
+
triangle.points << {:x => 5, :y => 6}
|
827
951
|
{:ruby}
|
828
952
|
|
953
|
+
## Array parameters
|
954
|
+
|
829
955
|
There are two different parameters that specify the length of the array.
|
830
956
|
|
831
957
|
`:initial_length`
|
@@ -877,6 +1003,49 @@ choice. The possible types of objects that a choice contains is
|
|
877
1003
|
controlled by the `:choices` parameter, while the `:selection` parameter
|
878
1004
|
specifies the active choice.
|
879
1005
|
|
1006
|
+
## Choice syntax
|
1007
|
+
|
1008
|
+
Choices have two ways of specifying the possible data objects they can
|
1009
|
+
contain. The `:choices` parameter or the block form. The block form is
|
1010
|
+
usually clearer and is prefered.
|
1011
|
+
|
1012
|
+
class MyInt16 < BinData::Record
|
1013
|
+
uint8 :e, :check_value => lambda { value == 0 or value == 1 }
|
1014
|
+
choice :int, :selection => :e,
|
1015
|
+
:choices => {0 => :int16be, 1 => :int16le}
|
1016
|
+
end
|
1017
|
+
-vs-
|
1018
|
+
|
1019
|
+
class MyInt16 < BinData::Record
|
1020
|
+
uint8 :e, :check_value => lambda { value == 0 or value == 1 }
|
1021
|
+
choice :int, :selection => :e do
|
1022
|
+
int16be 0
|
1023
|
+
int16le 1
|
1024
|
+
end
|
1025
|
+
end
|
1026
|
+
{:ruby}
|
1027
|
+
|
1028
|
+
The general form of the choice is
|
1029
|
+
|
1030
|
+
class MyRecord < BinData::Record
|
1031
|
+
choice :name, :selection => lambda { ... } do
|
1032
|
+
type key, :param1 => "foo", :param2 => "bar" ... # option 1
|
1033
|
+
type key, :param1 => "foo", :param2 => "bar" ... # option 2
|
1034
|
+
end
|
1035
|
+
end
|
1036
|
+
{:ruby}
|
1037
|
+
|
1038
|
+
`type`
|
1039
|
+
: is the name of a supplied type (e.g. `uint32be`, `string`)
|
1040
|
+
or a user defined type. This is the same as for Records.
|
1041
|
+
|
1042
|
+
`key`
|
1043
|
+
: is the value that `:selection` will return to specify that this
|
1044
|
+
choice is currently active. The key can be any ruby type (`String`,
|
1045
|
+
`Numeric` etc) except `Symbol`.
|
1046
|
+
|
1047
|
+
## Choice parameters
|
1048
|
+
|
880
1049
|
`:choices`
|
881
1050
|
|
882
1051
|
: Either an array or a hash specifying the possible data objects. The
|
@@ -904,21 +1073,23 @@ Examples
|
|
904
1073
|
|
905
1074
|
choices = {5 => type1, 17 => type2}
|
906
1075
|
obj = BinData::Choice.new(:choices => choices, :selection => 5)
|
907
|
-
obj
|
1076
|
+
obj # => "Type1"
|
908
1077
|
|
909
1078
|
choices = [ type1, type2 ]
|
910
1079
|
obj = BinData::Choice.new(:choices => choices, :selection => 1)
|
911
|
-
obj
|
1080
|
+
obj # => "Type2"
|
912
1081
|
|
913
1082
|
choices = [ nil, nil, nil, type1, nil, type2 ]
|
914
1083
|
obj = BinData::Choice.new(:choices => choices, :selection => 3)
|
915
|
-
obj
|
1084
|
+
obj # => "Type1"
|
916
1085
|
|
917
1086
|
class MyNumber < BinData::Record
|
918
1087
|
int8 :is_big_endian
|
919
|
-
choice :data, :
|
920
|
-
:
|
921
|
-
|
1088
|
+
choice :data, :selection => lambda { is_big_endian != 0 },
|
1089
|
+
:copy_on_change => true do
|
1090
|
+
int32be true
|
1091
|
+
int32le false
|
1092
|
+
end
|
922
1093
|
end
|
923
1094
|
|
924
1095
|
obj = MyNumber.new
|
@@ -936,7 +1107,7 @@ Examples
|
|
936
1107
|
|
937
1108
|
## Skipping over unused data
|
938
1109
|
|
939
|
-
Some
|
1110
|
+
Some structures contain binary data that is irrelevant to your purposes.
|
940
1111
|
|
941
1112
|
Say you are interested in 50 bytes of data located 10 megabytes into the
|
942
1113
|
stream. One way of accessing this useful data is:
|
@@ -962,6 +1133,49 @@ data and won't consume space in memory. When writing it will write
|
|
962
1133
|
end
|
963
1134
|
{:ruby}
|
964
1135
|
|
1136
|
+
## Bit-aligned Records
|
1137
|
+
|
1138
|
+
Most structured binary data is byte-aligned. Any bitfields that occur,
|
1139
|
+
usually start and end on a byte boundary. But occasionally you will
|
1140
|
+
come across a format where primitive types (string and numerics) are not
|
1141
|
+
aligned on byte boundaries.
|
1142
|
+
|
1143
|
+
class CrazyAlignment < BinData::Record
|
1144
|
+
bit4 :a
|
1145
|
+
string :b, :length => 2 # note: byte-aligned
|
1146
|
+
bit1 :c
|
1147
|
+
int16le :d # note: byte-aligned
|
1148
|
+
bit3 :e
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
c = CrazyAlignment.read("\xff" * 10)
|
1152
|
+
c.to_binary_s #=> "\360\377\377\200\377\377\340"
|
1153
|
+
{:ruby}
|
1154
|
+
|
1155
|
+
The above declaration does not work as expected because BinData's
|
1156
|
+
internal strings and integers are byte-aligned. We need bit-aligned
|
1157
|
+
versions of `string` and `int16le`.
|
1158
|
+
|
1159
|
+
class BitString < BinData::String
|
1160
|
+
bit_aligned
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
class BitInt16le < BinData::Int16le
|
1164
|
+
bit_aligned
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
class CrazyAlignment < BinData::Record
|
1168
|
+
bit4 :a
|
1169
|
+
bit_string :b, :length => 2
|
1170
|
+
bit1 :c
|
1171
|
+
bit_int16le :d
|
1172
|
+
bit3 :e
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
c = CrazyAlignment.read("\xff" * 10)
|
1176
|
+
c.to_binary_s #=> "\377\377\377\377\377"
|
1177
|
+
{:ruby}
|
1178
|
+
|
965
1179
|
## Wrappers
|
966
1180
|
|
967
1181
|
Sometimes you wish to create a new type that is simply an existing type
|
@@ -1004,15 +1218,15 @@ type. The `:type` parameter of `Array` is an example of a mandatory
|
|
1004
1218
|
type.
|
1005
1219
|
|
1006
1220
|
class IntArray < BinData::Wrapper
|
1007
|
-
mandatory_parameter :
|
1221
|
+
mandatory_parameter :byte_count
|
1008
1222
|
|
1009
|
-
array :type => :
|
1223
|
+
array :type => :uint16be, :initial_length => lambda { byte_count / 2 }
|
1010
1224
|
end
|
1011
1225
|
|
1012
1226
|
arr = IntArray.new
|
1013
|
-
#=> raises ArgumentError: parameter '
|
1227
|
+
#=> raises ArgumentError: parameter 'byte_count' must be specified in IntArray
|
1014
1228
|
|
1015
|
-
arr = IntArray.new(:
|
1229
|
+
arr = IntArray.new(:byte_count => 12)
|
1016
1230
|
arr.snapshot #=> [0, 0, 0, 0, 0, 0]
|
1017
1231
|
{:ruby}
|
1018
1232
|
|
@@ -1119,34 +1333,25 @@ what they are currently interested in.
|
|
1119
1333
|
There are several alternatives to BinData. Below is a comparison
|
1120
1334
|
between BinData and its alternatives.
|
1121
1335
|
|
1122
|
-
The short form is that BinData is the best choice for most cases.
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1336
|
+
The short form is that BinData is the best choice for most cases.
|
1337
|
+
It is the most full featured of all the alternatives. It is also
|
1338
|
+
arguably the most readable and easiest way to parse and write
|
1339
|
+
binary data.
|
1126
1340
|
|
1127
1341
|
### [BitStruct](http://rubyforge.org/projects/bit-struct)
|
1128
1342
|
|
1129
1343
|
BitStruct is the most complete of all the alternatives. It is
|
1130
|
-
declarative and supports
|
1131
|
-
|
1132
|
-
|
1344
|
+
declarative and supports most of the same primitive types as BinData.
|
1345
|
+
Its special feature is a self documenting feature for report generation.
|
1346
|
+
BitStruct's design choice is to favour speed over flexibility.
|
1133
1347
|
|
1134
1348
|
The major limitation of BitStruct is that it does not support variable
|
1135
|
-
length fields and dependent fields.
|
1136
|
-
|
1137
|
-
to the design choice to favour speed over flexibility.
|
1138
|
-
|
1139
|
-
Most non trivial file formats rely on dependent and variable length
|
1140
|
-
fields. It is difficult to use BitStruct with these formats as code
|
1141
|
-
must be written to explicitly handle the dependencies.
|
1142
|
-
|
1143
|
-
BitStruct does not currently support little endian bit fields, or
|
1144
|
-
bitfields that span more than 2 bytes. BitStruct is actively maintained
|
1145
|
-
so these limitations may be removed in a future release.
|
1349
|
+
length fields and dependent fields. This makes it difficult to work
|
1350
|
+
with any non trivial file formats.
|
1146
1351
|
|
1147
1352
|
If speed is important and you are only dealing with simple binary data
|
1148
|
-
types then BitStruct
|
1149
|
-
BinData is the better choice.
|
1353
|
+
types then BitStruct might be a good choice. For non trivial data
|
1354
|
+
types, BinData is the better choice.
|
1150
1355
|
|
1151
1356
|
### [BinaryParse](http://rubyforge.org/projects/binaryparse)
|
1152
1357
|
|