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.

Files changed (52) hide show
  1. data/ChangeLog +12 -0
  2. data/NEWS +53 -0
  3. data/Rakefile +2 -1
  4. data/examples/NBT.txt +149 -0
  5. data/examples/ip_address.rb +1 -2
  6. data/examples/list.rb +124 -0
  7. data/examples/nbt.rb +195 -0
  8. data/lib/bindata.rb +4 -3
  9. data/lib/bindata/alignment.rb +86 -0
  10. data/lib/bindata/array.rb +21 -29
  11. data/lib/bindata/base.rb +82 -81
  12. data/lib/bindata/base_primitive.rb +66 -48
  13. data/lib/bindata/choice.rb +18 -28
  14. data/lib/bindata/deprecated.rb +17 -0
  15. data/lib/bindata/dsl.rb +25 -15
  16. data/lib/bindata/int.rb +2 -2
  17. data/lib/bindata/io.rb +8 -6
  18. data/lib/bindata/offset.rb +91 -0
  19. data/lib/bindata/primitive.rb +22 -11
  20. data/lib/bindata/record.rb +40 -10
  21. data/lib/bindata/sanitize.rb +15 -30
  22. data/lib/bindata/string.rb +16 -17
  23. data/lib/bindata/stringz.rb +0 -1
  24. data/lib/bindata/struct.rb +17 -6
  25. data/lib/bindata/trace.rb +52 -0
  26. data/lib/bindata/wrapper.rb +28 -6
  27. data/manual.haml +56 -10
  28. data/manual.md +318 -113
  29. data/spec/alignment_spec.rb +61 -0
  30. data/spec/array_spec.rb +139 -178
  31. data/spec/base_primitive_spec.rb +86 -111
  32. data/spec/base_spec.rb +200 -172
  33. data/spec/bits_spec.rb +45 -53
  34. data/spec/choice_spec.rb +91 -87
  35. data/spec/deprecated_spec.rb +36 -14
  36. data/spec/float_spec.rb +16 -68
  37. data/spec/int_spec.rb +26 -27
  38. data/spec/io_spec.rb +105 -105
  39. data/spec/lazy_spec.rb +50 -50
  40. data/spec/primitive_spec.rb +36 -36
  41. data/spec/record_spec.rb +134 -134
  42. data/spec/registry_spec.rb +34 -38
  43. data/spec/rest_spec.rb +8 -11
  44. data/spec/skip_spec.rb +9 -17
  45. data/spec/spec_common.rb +4 -0
  46. data/spec/string_spec.rb +92 -115
  47. data/spec/stringz_spec.rb +41 -74
  48. data/spec/struct_spec.rb +132 -153
  49. data/spec/system_spec.rb +115 -60
  50. data/spec/wrapper_spec.rb +63 -31
  51. data/tasks/pkg.rake +1 -1
  52. 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
- Read on for the tutorial, or go straight to the
41
- [download](http://rubyforge.org/frs/?group_id=3252) page.
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 - 2010 [Dion Mendel](mailto:dion@lostrealm.com)
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 :num_ints, :check_value => lambda { value.even? }
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
- 1. A zero terminated string
77
- 2. An unsigned 8bit integer which must by even
78
- 3. A sequence of unsigned 32bit integers in big endian form, the total
79
- number of which is determined by the value of the 8bit integer.
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 of the data.
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
- str = BinData::Stringz::read("string1\0string2")
116
- str.snapshot #=> "string1"
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::Uint16be.new
124
- obj.read("\022\064")
125
- obj.value #=> 4660
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.value #=> 42
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 representation
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.assign(3)
204
- obj + 3 #=> 6
209
+ obj = BinData::Uint8.new(2)
210
+ obj.class #=> BinData::Uint8
211
+ obj + 3 #=> 5
205
212
 
206
- obj.snapshot #=> 3
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 either a
264
- `String` or a `Symbol`. If name is nil or the empty string, then
265
- this particular field is anonymous. An anonymous field is still
266
- read and written, but will not appear in `#snapshot`.
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 => :foo`
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 => :foo` is `:param => lambda { foo }`
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 can be difficult to read.
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
- ## Optional fields
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 aligned.
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.value #=> "abcde"
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.value #=> "abcdef"
730
+ obj #=> "abcdef"
656
731
 
657
732
  obj = BinData::String.new(:length => 6)
658
- obj.value = "abcd"
659
- obj.value #=> "abcd\000\000"
733
+ obj.assign("abcd")
734
+ obj #=> "abcd\000\000"
660
735
 
661
736
  obj = BinData::String.new(:length => 6)
662
- obj.value = "abcdefghij"
663
- obj.value #=> "abcdef"
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.value = "abcd"
674
- obj.value #=> "abcdAA"
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.value = "abcd"
686
- obj.value #=> "abcd"
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.value #=> "abcd"
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
- * `value_to_binary_string(value)`
749
- * `read_and_return_value(io)`
750
- * `sensible_default()`
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
- arr = BinData::Array.new(:type => :uint8)
818
- arr[3] = 5
819
- arr.snapshot #=> [0, 0, 0, 5]
910
+ class A < BinData::Record
911
+ array :numbers, :initial_length => 3 do
912
+ uint8
913
+ end
914
+ end
820
915
  {:ruby}
821
916
 
822
- Parameters can be passed to this object with a slightly clumsy syntax.
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
- arr = BinData::Array.new(:type => [:uint8, {:initial_value => :index}])
825
- arr[3] = 5
826
- arr.snapshot #=> [0, 1, 2, 5]
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.value # => "Type1"
1076
+ obj # => "Type1"
908
1077
 
909
1078
  choices = [ type1, type2 ]
910
1079
  obj = BinData::Choice.new(:choices => choices, :selection => 1)
911
- obj.value # => "Type2"
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.value # => "Type1"
1084
+ obj # => "Type1"
916
1085
 
917
1086
  class MyNumber < BinData::Record
918
1087
  int8 :is_big_endian
919
- choice :data, :choices => { true => :int32be, false => :int32le },
920
- :selection => lambda { is_big_endian != 0 },
921
- :copy_on_change => true
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 binary structures contain data that is irrelevant to your purposes.
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 :half_count
1221
+ mandatory_parameter :byte_count
1008
1222
 
1009
- array :type => :uint8, :initial_length => lambda { half_count * 2 }
1223
+ array :type => :uint16be, :initial_length => lambda { byte_count / 2 }
1010
1224
  end
1011
1225
 
1012
1226
  arr = IntArray.new
1013
- #=> raises ArgumentError: parameter 'half_count' must be specified in IntArray
1227
+ #=> raises ArgumentError: parameter 'byte_count' must be specified in IntArray
1014
1228
 
1015
- arr = IntArray.new(:half_count => lambda { 1 + 2 })
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. If
1123
- decoding / encoding speed is very important and the binary formats are
1124
- simple then BitStruct may be a good choice. (Though if speed is
1125
- important, perhaps you should investigate a language other than Ruby.)
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 all the same primitive types as BinData. In
1131
- addition it includes a self documenting feature to make it easy to write
1132
- reports.
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. The simple PascalString example
1136
- used previously is not possible with BitStruct. This limitation is due
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 is a good choice. For non trivial data types,
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