bindata 1.8.3 → 2.0.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.
- checksums.yaml +7 -0
- data/.travis.yml +1 -1
- data/ChangeLog.rdoc +5 -14
- data/INSTALL +10 -9
- data/NEWS.rdoc +19 -0
- data/README.md +2 -6
- data/Rakefile +0 -2
- data/bindata.gemspec +0 -3
- data/examples/gzip.rb +70 -106
- data/examples/tcp_ip.rb +8 -10
- data/lib/bindata.rb +4 -0
- data/lib/bindata/base.rb +5 -10
- data/lib/bindata/bits.rb +15 -84
- data/lib/bindata/buffer.rb +1 -1
- data/lib/bindata/choice.rb +24 -8
- data/lib/bindata/deprecated.rb +3 -26
- data/lib/bindata/int.rb +10 -2
- data/lib/bindata/io.rb +5 -17
- data/lib/bindata/lazy.rb +1 -1
- data/lib/bindata/params.rb +1 -1
- data/lib/bindata/record.rb +19 -1
- data/lib/bindata/sanitize.rb +0 -20
- data/lib/bindata/string.rb +4 -5
- data/lib/bindata/struct.rb +23 -124
- data/lib/bindata/version.rb +1 -1
- data/test/alignment_test.rb +1 -1
- data/test/array_test.rb +3 -3
- data/test/bits_test.rb +34 -38
- data/test/common.rb +1 -5
- data/test/io_test.rb +0 -15
- data/test/lazy_test.rb +3 -11
- data/test/record_test.rb +21 -27
- data/test/string_test.rb +23 -25
- data/test/struct_test.rb +21 -52
- data/test/system_test.rb +9 -9
- metadata +48 -119
- data/doc/manual.haml +0 -407
- data/doc/manual.md +0 -1682
- data/setup.rb +0 -1585
- data/tasks/manual.rake +0 -36
data/doc/manual.md
DELETED
@@ -1,1682 +0,0 @@
|
|
1
|
-
Title: BinData Reference Manual
|
2
|
-
|
3
|
-
{:ruby: lang=ruby html_use_syntax=true}
|
4
|
-
|
5
|
-
# BinData - Parsing Binary Data in Ruby
|
6
|
-
|
7
|
-
A declarative way to read and write structured binary data.
|
8
|
-
|
9
|
-
## What is it for?
|
10
|
-
|
11
|
-
Do you ever find yourself writing code like this?
|
12
|
-
|
13
|
-
io = File.open(...)
|
14
|
-
len = io.read(2).unpack("v")[0]
|
15
|
-
name = io.read(len)
|
16
|
-
width, height = io.read(8).unpack("VV")
|
17
|
-
puts "Rectangle #{name} is #{width} x #{height}"
|
18
|
-
{:ruby}
|
19
|
-
|
20
|
-
It's ugly, violates DRY and feels like you're writing Perl, not Ruby.
|
21
|
-
|
22
|
-
There is a better way.
|
23
|
-
|
24
|
-
class Rectangle < BinData::Record
|
25
|
-
endian :little
|
26
|
-
uint16 :len
|
27
|
-
string :name, :read_length => :len
|
28
|
-
uint32 :width
|
29
|
-
uint32 :height
|
30
|
-
end
|
31
|
-
|
32
|
-
io = File.open(...)
|
33
|
-
r = Rectangle.read(io)
|
34
|
-
puts "Rectangle #{r.name} is #{r.width} x #{r.height}"
|
35
|
-
{:ruby}
|
36
|
-
|
37
|
-
BinData makes it easy to specify the structure of the data you are
|
38
|
-
manipulating.
|
39
|
-
|
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
|
-
|
43
|
-
Last updated: 2013-09-02
|
44
|
-
|
45
|
-
## Source code
|
46
|
-
|
47
|
-
[BinData](http://github.com/dmendel/bindata) is hosted on Github.
|
48
|
-
|
49
|
-
## License
|
50
|
-
|
51
|
-
BinData is released under the same license as Ruby.
|
52
|
-
|
53
|
-
Copyright © 2007 - 2013 [Dion Mendel](mailto:dion@lostrealm.com)
|
54
|
-
|
55
|
-
## Donate
|
56
|
-
|
57
|
-
Want to donate? My favourite local charity is
|
58
|
-
[Perth Raptor Care](http://care.raptor.id.au/help.html#PAL).
|
59
|
-
|
60
|
-
---------------------------------------------------------------------------
|
61
|
-
|
62
|
-
# Installation
|
63
|
-
|
64
|
-
You can install BinData via rubygems (recommended).
|
65
|
-
|
66
|
-
gem install bindata
|
67
|
-
|
68
|
-
or as source package.
|
69
|
-
|
70
|
-
git clone http://github.com/dmendel/bindata.git
|
71
|
-
cd bindata && ruby setup.rb
|
72
|
-
|
73
|
-
---------------------------------------------------------------------------
|
74
|
-
|
75
|
-
# Overview
|
76
|
-
|
77
|
-
BinData declarations are easy to read. Here's an example.
|
78
|
-
|
79
|
-
class MyFancyFormat < BinData::Record
|
80
|
-
stringz :comment
|
81
|
-
uint8 :len
|
82
|
-
array :data, :type => :int32be, :initial_length => :len
|
83
|
-
end
|
84
|
-
{:ruby}
|
85
|
-
|
86
|
-
This fancy format describes the following collection of data:
|
87
|
-
|
88
|
-
`:comment`
|
89
|
-
: A zero terminated string
|
90
|
-
|
91
|
-
`:len`
|
92
|
-
: An unsigned 8bit integer
|
93
|
-
|
94
|
-
`:data`
|
95
|
-
: A sequence of unsigned 32bit big endian integers. The number of
|
96
|
-
integers is given by the value of `:len`
|
97
|
-
|
98
|
-
The BinData declaration matches the English description closely.
|
99
|
-
Compare the above declaration with the equivalent `#unpack` code to read
|
100
|
-
such a data record.
|
101
|
-
|
102
|
-
def read_fancy_format(io)
|
103
|
-
comment, len, rest = io.read.unpack("Z*Ca*")
|
104
|
-
data = rest.unpack("N#{len}")
|
105
|
-
{:comment => comment, :len => len, :data => *data}
|
106
|
-
end
|
107
|
-
{:ruby}
|
108
|
-
|
109
|
-
The BinData declaration clearly shows the structure of the record. The
|
110
|
-
`#unpack` code makes this structure opaque.
|
111
|
-
|
112
|
-
The general usage of BinData is to declare a structured collection of
|
113
|
-
data as a user defined record. This record can be instantiated, read,
|
114
|
-
written and manipulated without the user having to be concerned with the
|
115
|
-
underlying binary data representation.
|
116
|
-
|
117
|
-
---------------------------------------------------------------------------
|
118
|
-
|
119
|
-
# Records
|
120
|
-
|
121
|
-
The general format of a BinData record declaration is a class containing
|
122
|
-
one or more fields.
|
123
|
-
|
124
|
-
class MyName < BinData::Record
|
125
|
-
type field_name, :param1 => "foo", :param2 => bar, ...
|
126
|
-
...
|
127
|
-
end
|
128
|
-
{:ruby}
|
129
|
-
|
130
|
-
`type`
|
131
|
-
: is the name of a supplied type (e.g. `uint32be`, `string`, `array`)
|
132
|
-
or a user defined type. For user defined types, the class name is
|
133
|
-
converted from `CamelCase` to lowercased `underscore_style`.
|
134
|
-
|
135
|
-
`field_name`
|
136
|
-
: is the name by which you can access the field. Use a `Symbol` for
|
137
|
-
the name. If the name is omitted, then this particular field
|
138
|
-
is anonymous. An anonymous field is still read and written, but
|
139
|
-
will not appear in `#snapshot`.
|
140
|
-
|
141
|
-
Each field may have optional *parameters* for how to process the data.
|
142
|
-
The parameters are passed as a `Hash` with `Symbols` for keys.
|
143
|
-
Parameters are designed to be lazily evaluated, possibly multiple times.
|
144
|
-
This means that any parameter value must not have side effects.
|
145
|
-
|
146
|
-
Here are some examples of legal values for parameters.
|
147
|
-
|
148
|
-
* `:param => 5`
|
149
|
-
* `:param => lambda { foo + 2 }`
|
150
|
-
* `:param => :bar`
|
151
|
-
|
152
|
-
The simplest case is when the value is a literal value, such as `5`.
|
153
|
-
|
154
|
-
If the value is not a literal, it is expected to be a lambda. The
|
155
|
-
lambda will be evaluated in the context of the parent. In this case
|
156
|
-
the parent is an instance of `MyName`.
|
157
|
-
|
158
|
-
If the value is a symbol, it is taken as syntactic sugar for a lambda
|
159
|
-
containing the value of the symbol.
|
160
|
-
e.g `:param => :bar` is `:param => lambda { bar }`
|
161
|
-
|
162
|
-
## Specifying default endian
|
163
|
-
|
164
|
-
The endianess of numeric types must be explicitly defined so that the
|
165
|
-
code produced is independent of architecture. However, explicitly
|
166
|
-
specifying the endian for each numeric field can result in a bloated
|
167
|
-
declaration that is difficult to read.
|
168
|
-
|
169
|
-
class A < BinData::Record
|
170
|
-
int16be :a
|
171
|
-
int32be :b
|
172
|
-
int16le :c # <-- Note little endian!
|
173
|
-
int32be :d
|
174
|
-
float_be :e
|
175
|
-
array :f, :type => :uint32be
|
176
|
-
end
|
177
|
-
{:ruby}
|
178
|
-
|
179
|
-
The `endian` keyword can be used to set the default endian. This makes
|
180
|
-
the declaration easier to read. Any numeric field that doesn't use the
|
181
|
-
default endian can explicitly override it.
|
182
|
-
|
183
|
-
class A < BinData::Record
|
184
|
-
endian :big
|
185
|
-
|
186
|
-
int16 :a
|
187
|
-
int32 :b
|
188
|
-
int16le :c # <-- Note how this little endian now stands out
|
189
|
-
int32 :d
|
190
|
-
float :e
|
191
|
-
array :f, :type => :uint32
|
192
|
-
end
|
193
|
-
{:ruby}
|
194
|
-
|
195
|
-
The increase in clarity can be seen with the above example. The
|
196
|
-
`endian` keyword will cascade to nested types, as illustrated with the
|
197
|
-
array in the above example.
|
198
|
-
|
199
|
-
The endian keyword can also be used to identify custom types that have
|
200
|
-
endianness. To do this, the class name of the custom types must end with `Le`
|
201
|
-
for little endian, and `Be` for big endian.
|
202
|
-
|
203
|
-
class CoordLe < BinData::Record
|
204
|
-
endian :little
|
205
|
-
int16 :x
|
206
|
-
int16 :y
|
207
|
-
end
|
208
|
-
|
209
|
-
class CoordBe < BinData::Record
|
210
|
-
endian :big
|
211
|
-
int16 :x
|
212
|
-
int16 :y
|
213
|
-
end
|
214
|
-
|
215
|
-
class Rectangle < BinData::Record
|
216
|
-
endian :little
|
217
|
-
|
218
|
-
coord :upper_left
|
219
|
-
coord :lower_right
|
220
|
-
end
|
221
|
-
{:ruby}
|
222
|
-
|
223
|
-
|
224
|
-
## Dependencies between fields
|
225
|
-
|
226
|
-
A common occurence in binary file formats is one field depending upon
|
227
|
-
the value of another. e.g. A string preceded by its length.
|
228
|
-
|
229
|
-
As an example, let's assume a Pascal style string where the byte
|
230
|
-
preceding the string contains the string's length.
|
231
|
-
|
232
|
-
# reading
|
233
|
-
io = File.open(...)
|
234
|
-
len = io.getc
|
235
|
-
str = io.read(len)
|
236
|
-
puts "string is " + str
|
237
|
-
|
238
|
-
# writing
|
239
|
-
io = File.open(...)
|
240
|
-
str = "this is a string"
|
241
|
-
io.putc(str.length)
|
242
|
-
io.write(str)
|
243
|
-
{:ruby}
|
244
|
-
|
245
|
-
Here's how we'd implement the same example with BinData.
|
246
|
-
|
247
|
-
class PascalString < BinData::Record
|
248
|
-
uint8 :len, :value => lambda { data.length }
|
249
|
-
string :data, :read_length => :len
|
250
|
-
end
|
251
|
-
|
252
|
-
# reading
|
253
|
-
io = File.open(...)
|
254
|
-
ps = PascalString.new
|
255
|
-
ps.read(io)
|
256
|
-
puts "string is " + ps.data
|
257
|
-
|
258
|
-
# writing
|
259
|
-
io = File.open(...)
|
260
|
-
ps = PascalString.new
|
261
|
-
ps.data = "this is a string"
|
262
|
-
ps.write(io)
|
263
|
-
{:ruby}
|
264
|
-
|
265
|
-
This syntax needs explaining. Let's simplify by examining reading and
|
266
|
-
writing separately.
|
267
|
-
|
268
|
-
class PascalStringReader < BinData::Record
|
269
|
-
uint8 :len
|
270
|
-
string :data, :read_length => :len
|
271
|
-
end
|
272
|
-
{:ruby}
|
273
|
-
|
274
|
-
This states that when reading the string, the initial length of the
|
275
|
-
string (and hence the number of bytes to read) is determined by the
|
276
|
-
value of the `len` field.
|
277
|
-
|
278
|
-
Note that `:read_length => :len` is syntactic sugar for
|
279
|
-
`:read_length => lambda { len }`, as described previously.
|
280
|
-
|
281
|
-
class PascalStringWriter < BinData::Record
|
282
|
-
uint8 :len, :value => lambda { data.length }
|
283
|
-
string :data
|
284
|
-
end
|
285
|
-
{:ruby}
|
286
|
-
|
287
|
-
This states that the value of `len` is always equal to the length of
|
288
|
-
`data`. `len` may not be manually modified.
|
289
|
-
|
290
|
-
Combining these two definitions gives the definition for `PascalString`
|
291
|
-
as previously defined.
|
292
|
-
|
293
|
-
It is important to note with dependencies, that a field can only depend
|
294
|
-
on one before it. You can't have a string which has the characters
|
295
|
-
first and the length afterwards.
|
296
|
-
|
297
|
-
## Nested Records
|
298
|
-
|
299
|
-
BinData supports anonymous nested records. The `struct` keyword declares
|
300
|
-
a nested structure that can be used to imply a grouping of related data.
|
301
|
-
|
302
|
-
class LabeledCoord < BinData::Record
|
303
|
-
string :label, :length => 20
|
304
|
-
|
305
|
-
struct :coord do
|
306
|
-
endian :little
|
307
|
-
double :x
|
308
|
-
double :z
|
309
|
-
double :y
|
310
|
-
end
|
311
|
-
end
|
312
|
-
|
313
|
-
pos = LabeledCoord.new(:label => "red leader")
|
314
|
-
pos.coord.assign(:x => 2.0, :y => 0, :z => -1.57)
|
315
|
-
{:ruby}
|
316
|
-
|
317
|
-
This nested structure can be put in its own class and reused.
|
318
|
-
The above example can also be declared as:
|
319
|
-
|
320
|
-
class Coord < BinData::Record
|
321
|
-
endian :little
|
322
|
-
double :x
|
323
|
-
double :z
|
324
|
-
double :y
|
325
|
-
end
|
326
|
-
|
327
|
-
class LabeledCoord < BinData::Record
|
328
|
-
string :label, :length => 20
|
329
|
-
coord :coord
|
330
|
-
end
|
331
|
-
{:ruby}
|
332
|
-
|
333
|
-
## Optional fields
|
334
|
-
|
335
|
-
A record may contain optional fields. The optional state of a field is
|
336
|
-
decided by the `:onlyif` parameter. If the value of this parameter is
|
337
|
-
`false`, then the field will be as if it didn't exist in the record.
|
338
|
-
|
339
|
-
class RecordWithOptionalField < BinData::Record
|
340
|
-
...
|
341
|
-
uint8 :comment_flag
|
342
|
-
string :comment, :length => 20, :onlyif => :has_comment?
|
343
|
-
|
344
|
-
def has_comment?
|
345
|
-
comment_flag.nonzero?
|
346
|
-
end
|
347
|
-
end
|
348
|
-
{:ruby}
|
349
|
-
|
350
|
-
In the above example, the `comment` field is only included in the record
|
351
|
-
if the value of the `comment_flag` field is non zero.
|
352
|
-
|
353
|
-
---------------------------------------------------------------------------
|
354
|
-
|
355
|
-
# Primitive Types
|
356
|
-
|
357
|
-
BinData provides support for the most commonly used primitive types that
|
358
|
-
are used when working with binary data. Namely:
|
359
|
-
|
360
|
-
* fixed size strings
|
361
|
-
* zero terminated strings
|
362
|
-
* byte based integers - signed or unsigned, big or little endian and
|
363
|
-
of any size
|
364
|
-
* bit based integers - unsigned big or little endian integers of any
|
365
|
-
size
|
366
|
-
* floating point numbers - single or double precision floats in either
|
367
|
-
big or little endian
|
368
|
-
|
369
|
-
Primitives may be manipulated individually, but is more common to work
|
370
|
-
with them as part of a record.
|
371
|
-
|
372
|
-
Examples of individual usage:
|
373
|
-
|
374
|
-
int16 = BinData::Int16be.new(941)
|
375
|
-
int16.to_binary_s #=> "\003\255"
|
376
|
-
|
377
|
-
fl = BinData::FloatBe.read("\100\055\370\124") #=> 2.71828174591064
|
378
|
-
fl.num_bytes #=> 4
|
379
|
-
|
380
|
-
fl * int16 #=> 2557.90320057996
|
381
|
-
{:ruby}
|
382
|
-
|
383
|
-
There are several parameters that are specific to all primitives.
|
384
|
-
|
385
|
-
`:initial_value`
|
386
|
-
|
387
|
-
: This contains the initial value that the primitive will contain
|
388
|
-
after initialization. This is useful for setting default values.
|
389
|
-
|
390
|
-
obj = BinData::String.new(:initial_value => "hello ")
|
391
|
-
obj + "world" #=> "hello world"
|
392
|
-
|
393
|
-
obj.assign("good-bye " )
|
394
|
-
obj + "world" #=> "good-bye world"
|
395
|
-
{:ruby}
|
396
|
-
|
397
|
-
`:value`
|
398
|
-
|
399
|
-
: The primitive will always contain this value. Reading or assigning
|
400
|
-
will not change the value. This parameter is used to define
|
401
|
-
constants or dependent fields.
|
402
|
-
|
403
|
-
pi = BinData::FloatLe.new(:value => Math::PI)
|
404
|
-
pi.assign(3)
|
405
|
-
puts pi #=> 3.14159265358979
|
406
|
-
|
407
|
-
|
408
|
-
class IntList < BinData::Record
|
409
|
-
uint8 :len, :value => lambda { data.length }
|
410
|
-
array :data, :type => :uint32be
|
411
|
-
end
|
412
|
-
|
413
|
-
list = IntList.new([1, 2, 3])
|
414
|
-
list.len #=> 3
|
415
|
-
{:ruby}
|
416
|
-
|
417
|
-
`:assert`
|
418
|
-
|
419
|
-
: When reading or assigning, will raise a `ValidityError` if the value
|
420
|
-
read or assigned does not match the value of this parameter.
|
421
|
-
|
422
|
-
obj = BinData::String.new(:assert => lambda { /aaa/ =~ value })
|
423
|
-
obj.read("baaa!") #=> "baaa!"
|
424
|
-
obj.read("bbb") #=> raises ValidityError
|
425
|
-
|
426
|
-
obj = BinData::String.new(:assert => "foo")
|
427
|
-
obj.read("foo") #=> "foo"
|
428
|
-
obj.assign("bar") #=> raises ValidityError
|
429
|
-
{:ruby}
|
430
|
-
|
431
|
-
`:asserted_value`
|
432
|
-
|
433
|
-
: A combination of `:assert` and `:value`. Used as a shortcut when
|
434
|
-
both `:assert` and `:value` have the same values. The following
|
435
|
-
are equivalent.
|
436
|
-
|
437
|
-
obj = BinData::Uint32be.new(:assert => 42, :value => 42)
|
438
|
-
obj = BinData::Uint32be.new(:asserted_value => 42)
|
439
|
-
{:ruby}
|
440
|
-
|
441
|
-
## Numerics
|
442
|
-
|
443
|
-
There are three kinds of numeric types that are supported by BinData.
|
444
|
-
|
445
|
-
### Byte based integers
|
446
|
-
|
447
|
-
These are the common integers that are used in most low level
|
448
|
-
programming languages (C, C++, Java etc). These integers can be signed
|
449
|
-
or unsigned. The endian must be specified so that the conversion is
|
450
|
-
independent of architecture. The bit size of these integers must be a
|
451
|
-
multiple of 8. Examples of byte based integers are:
|
452
|
-
|
453
|
-
`uint16be`
|
454
|
-
: unsigned 16 bit big endian integer
|
455
|
-
|
456
|
-
`int8`
|
457
|
-
: signed 8 bit integer
|
458
|
-
|
459
|
-
`int32le`
|
460
|
-
: signed 32 bit little endian integer
|
461
|
-
|
462
|
-
`uint40be`
|
463
|
-
: unsigned 40 bit big endian integer
|
464
|
-
|
465
|
-
The `be` | `le` suffix may be omitted if the `endian` keyword is in use.
|
466
|
-
|
467
|
-
### Bit based integers
|
468
|
-
|
469
|
-
These unsigned integers are used to define bitfields in records.
|
470
|
-
Bitfields are big endian by default but little endian may be specified
|
471
|
-
explicitly. Little endian bitfields are rare, but do occur in older
|
472
|
-
file formats (e.g. The file allocation table for FAT12 filesystems is
|
473
|
-
stored as an array of 12bit little endian integers).
|
474
|
-
|
475
|
-
An array of bit based integers will be packed according to their endian.
|
476
|
-
|
477
|
-
In a record, adjacent bitfields will be packed according to their
|
478
|
-
endian. All other fields are byte-aligned.
|
479
|
-
|
480
|
-
Examples of bit based integers are:
|
481
|
-
|
482
|
-
`bit1`
|
483
|
-
: 1 bit big endian integer (may be used as boolean)
|
484
|
-
|
485
|
-
`bit4_le`
|
486
|
-
: 4 bit little endian integer
|
487
|
-
|
488
|
-
`bit32`
|
489
|
-
: 32 bit big endian integer
|
490
|
-
|
491
|
-
The difference between byte and bit base integers of the same number of
|
492
|
-
bits (e.g. `uint8` vs `bit8`) is one of alignment.
|
493
|
-
|
494
|
-
This example is packed as 3 bytes
|
495
|
-
|
496
|
-
class A < BinData::Record
|
497
|
-
bit4 :a
|
498
|
-
uint8 :b
|
499
|
-
bit4 :c
|
500
|
-
end
|
501
|
-
|
502
|
-
Data is stored as: AAAA0000 BBBBBBBB CCCC0000
|
503
|
-
{:ruby}
|
504
|
-
|
505
|
-
Whereas this example is packed into only 2 bytes
|
506
|
-
|
507
|
-
class B < BinData::Record
|
508
|
-
bit4 :a
|
509
|
-
bit8 :b
|
510
|
-
bit4 :c
|
511
|
-
end
|
512
|
-
|
513
|
-
Data is stored as: AAAABBBB BBBBCCCC
|
514
|
-
{:ruby}
|
515
|
-
|
516
|
-
### Floating point numbers
|
517
|
-
|
518
|
-
BinData supports 32 and 64 bit floating point numbers, in both big and
|
519
|
-
little endian format. These types are:
|
520
|
-
|
521
|
-
`float_le`
|
522
|
-
: single precision 32 bit little endian float
|
523
|
-
|
524
|
-
`float_be`
|
525
|
-
: single precision 32 bit big endian float
|
526
|
-
|
527
|
-
`double_le`
|
528
|
-
: double precision 64 bit little endian float
|
529
|
-
|
530
|
-
`double_be`
|
531
|
-
: double precision 64 bit big endian float
|
532
|
-
|
533
|
-
The `_be` | `_le` suffix may be omitted if the `endian` keyword is in use.
|
534
|
-
|
535
|
-
### Example
|
536
|
-
|
537
|
-
Here is an example declaration for an Internet Protocol network packet.
|
538
|
-
|
539
|
-
class IP_PDU < BinData::Record
|
540
|
-
endian :big
|
541
|
-
|
542
|
-
bit4 :version, :value => 4
|
543
|
-
bit4 :header_length
|
544
|
-
uint8 :tos
|
545
|
-
uint16 :total_length
|
546
|
-
uint16 :ident
|
547
|
-
bit3 :flags
|
548
|
-
bit13 :frag_offset
|
549
|
-
uint8 :ttl
|
550
|
-
uint8 :protocol
|
551
|
-
uint16 :checksum
|
552
|
-
uint32 :src_addr
|
553
|
-
uint32 :dest_addr
|
554
|
-
string :options, :read_length => :options_length_in_bytes
|
555
|
-
string :data, :read_length => lambda { total_length - header_length_in_bytes }
|
556
|
-
|
557
|
-
def header_length_in_bytes
|
558
|
-
header_length * 4
|
559
|
-
end
|
560
|
-
|
561
|
-
def options_length_in_bytes
|
562
|
-
header_length_in_bytes - 20
|
563
|
-
end
|
564
|
-
end
|
565
|
-
{:ruby}
|
566
|
-
|
567
|
-
Three of the fields have parameters.
|
568
|
-
* The version field always has the value 4, as per the standard.
|
569
|
-
* The options field is read as a raw string, but not processed.
|
570
|
-
* The data field contains the payload of the packet. Its length is
|
571
|
-
calculated as the total length of the packet minus the length of
|
572
|
-
the header.
|
573
|
-
|
574
|
-
## Strings
|
575
|
-
|
576
|
-
BinData supports two types of strings - fixed size and zero terminated.
|
577
|
-
Strings are treated internally as a sequence of 8bit bytes. This is the
|
578
|
-
same as strings in Ruby 1.8. BinData fully supports Ruby 1.9 string
|
579
|
-
encodings. See this [FAQ
|
580
|
-
entry](#im_using_ruby_19_how_do_i_use_string_encodings_with_bindata) for
|
581
|
-
details.
|
582
|
-
|
583
|
-
### Fixed Sized Strings
|
584
|
-
|
585
|
-
Fixed sized strings may have a set length (in bytes). If an assigned
|
586
|
-
value is shorter than this length, it will be padded to this length. If
|
587
|
-
no length is set, the length is taken to be the length of the assigned
|
588
|
-
value.
|
589
|
-
|
590
|
-
There are several parameters that are specific to fixed sized strings.
|
591
|
-
|
592
|
-
`:read_length`
|
593
|
-
|
594
|
-
: The length in bytes to use when reading a value.
|
595
|
-
|
596
|
-
obj = BinData::String.new(:read_length => 5)
|
597
|
-
obj.read("abcdefghij")
|
598
|
-
obj #=> "abcde"
|
599
|
-
{:ruby}
|
600
|
-
|
601
|
-
`:length`
|
602
|
-
|
603
|
-
: The fixed length of the string. If a shorter string is set, it
|
604
|
-
will be padded to this length. Longer strings will be truncated.
|
605
|
-
|
606
|
-
obj = BinData::String.new(:length => 6)
|
607
|
-
obj.read("abcdefghij")
|
608
|
-
obj #=> "abcdef"
|
609
|
-
|
610
|
-
obj = BinData::String.new(:length => 6)
|
611
|
-
obj.assign("abcd")
|
612
|
-
obj #=> "abcd\000\000"
|
613
|
-
|
614
|
-
obj = BinData::String.new(:length => 6)
|
615
|
-
obj.assign("abcdefghij")
|
616
|
-
obj #=> "abcdef"
|
617
|
-
{:ruby}
|
618
|
-
|
619
|
-
`:pad_front` or `:pad_left`
|
620
|
-
|
621
|
-
: Boolean, default `false`. Signifies that the padding occurs at the front
|
622
|
-
of the string rather than the end.
|
623
|
-
|
624
|
-
obj = BinData::String.new(:length => 6, :pad_front => true)
|
625
|
-
obj.assign("abcd")
|
626
|
-
obj.snapshot #=> "\000\000abcd"
|
627
|
-
{:ruby}
|
628
|
-
|
629
|
-
`:pad_byte`
|
630
|
-
|
631
|
-
: Defaults to `"\0"`. The character to use when padding a string to a
|
632
|
-
set length. Valid values are `Integers` and `Strings` of one byte.
|
633
|
-
Multi byte padding is not supported.
|
634
|
-
|
635
|
-
obj = BinData::String.new(:length => 6, :pad_byte => 'A')
|
636
|
-
obj.assign("abcd")
|
637
|
-
obj.snapshot #=> "abcdAA"
|
638
|
-
obj.to_binary_s #=> "abcdAA"
|
639
|
-
{:ruby}
|
640
|
-
|
641
|
-
`:trim_padding`
|
642
|
-
|
643
|
-
: Boolean, default `false`. If set, the value of this string will
|
644
|
-
have all pad_bytes trimmed from the end of the string. The value
|
645
|
-
will not be trimmed when writing.
|
646
|
-
|
647
|
-
obj = BinData::String.new(:length => 6, :trim_value => true)
|
648
|
-
obj.assign("abcd")
|
649
|
-
obj.snapshot #=> "abcd"
|
650
|
-
obj.to_binary_s #=> "abcd\000\000"
|
651
|
-
{:ruby}
|
652
|
-
|
653
|
-
### Zero Terminated Strings
|
654
|
-
|
655
|
-
These strings are modelled on the C style of string - a sequence of
|
656
|
-
bytes terminated by a null (`"\0"`) byte.
|
657
|
-
|
658
|
-
obj = BinData::Stringz.new
|
659
|
-
obj.read("abcd\000efgh")
|
660
|
-
obj #=> "abcd"
|
661
|
-
obj.num_bytes #=> 5
|
662
|
-
obj.to_binary_s #=> "abcd\000"
|
663
|
-
{:ruby}
|
664
|
-
|
665
|
-
## User Defined Primitive Types
|
666
|
-
|
667
|
-
Most user defined types will be Records but occasionally we'd like to
|
668
|
-
create a custom primitive type.
|
669
|
-
|
670
|
-
Let us revisit the Pascal String example.
|
671
|
-
|
672
|
-
class PascalString < BinData::Record
|
673
|
-
uint8 :len, :value => lambda { data.length }
|
674
|
-
string :data, :read_length => :len
|
675
|
-
end
|
676
|
-
{:ruby}
|
677
|
-
|
678
|
-
We'd like to make `PascalString` a user defined type that behaves like a
|
679
|
-
`BinData::BasePrimitive` object so we can use `:initial_value` etc.
|
680
|
-
Here's an example usage of what we'd like:
|
681
|
-
|
682
|
-
class Favourites < BinData::Record
|
683
|
-
pascal_string :language, :initial_value => "ruby"
|
684
|
-
pascal_string :os, :initial_value => "unix"
|
685
|
-
end
|
686
|
-
|
687
|
-
f = Favourites.new
|
688
|
-
f.os = "freebsd"
|
689
|
-
f.to_binary_s #=> "\004ruby\007freebsd"
|
690
|
-
{:ruby}
|
691
|
-
|
692
|
-
We create this type of custom string by inheriting from
|
693
|
-
`BinData::Primitive` (instead of `BinData::Record`) and implementing the
|
694
|
-
`#get` and `#set` methods.
|
695
|
-
|
696
|
-
class PascalString < BinData::Primitive
|
697
|
-
uint8 :len, :value => lambda { data.length }
|
698
|
-
string :data, :read_length => :len
|
699
|
-
|
700
|
-
def get; self.data; end
|
701
|
-
def set(v) self.data = v; end
|
702
|
-
end
|
703
|
-
{:ruby}
|
704
|
-
|
705
|
-
A user defined primitive type has both an internal (binary structure)
|
706
|
-
and an external (ruby interface) representation. The internal
|
707
|
-
representation is encapsulated and inaccessible from the external ruby
|
708
|
-
interface.
|
709
|
-
|
710
|
-
Consider a LispBool type that uses `:t` for true and `nil` for false.
|
711
|
-
The binary representation is a signed byte with value `1` for true and
|
712
|
-
`-1` for false.
|
713
|
-
|
714
|
-
class LispBool < BinData::Primitive
|
715
|
-
int8 :val
|
716
|
-
|
717
|
-
def get
|
718
|
-
case self.val
|
719
|
-
when 1
|
720
|
-
:t
|
721
|
-
when -1
|
722
|
-
nil
|
723
|
-
else
|
724
|
-
nil # unknown value, default to false
|
725
|
-
end
|
726
|
-
end
|
727
|
-
|
728
|
-
def set(v)
|
729
|
-
case v
|
730
|
-
when :t
|
731
|
-
self.val = 1
|
732
|
-
when nil
|
733
|
-
self.val = -1
|
734
|
-
else
|
735
|
-
self.val = -1 # unknown value, default to false
|
736
|
-
end
|
737
|
-
end
|
738
|
-
end
|
739
|
-
|
740
|
-
b = LispBool.new
|
741
|
-
|
742
|
-
b.assign(:t)
|
743
|
-
b.to_binary_s #=> "\001"
|
744
|
-
|
745
|
-
b.read("\xff")
|
746
|
-
b.snapshot #=> nil
|
747
|
-
{:ruby}
|
748
|
-
|
749
|
-
`#read` and `#write` use the internal representation. `#assign` and
|
750
|
-
`#snapshot` use the external representation. Mixing them up will lead
|
751
|
-
to undefined behaviour.
|
752
|
-
|
753
|
-
b = LispBool.new
|
754
|
-
b.assign(1) #=> undefined. Don't do this.
|
755
|
-
{:ruby}
|
756
|
-
|
757
|
-
### Advanced User Defined Primitive Types
|
758
|
-
|
759
|
-
Sometimes a user defined primitive type can not easily be declaratively
|
760
|
-
defined. In this case you should inherit from `BinData::BasePrimitive`
|
761
|
-
and implement the following three methods:
|
762
|
-
|
763
|
-
`def value_to_binary_string(value)`
|
764
|
-
|
765
|
-
: Takes a ruby value (`String`, `Numeric` etc) and converts it to
|
766
|
-
the appropriate binary string representation.
|
767
|
-
|
768
|
-
`def read_and_return_value(io)`
|
769
|
-
|
770
|
-
: Reads a number of bytes from `io` and returns a ruby object that
|
771
|
-
represents these bytes.
|
772
|
-
|
773
|
-
`def sensible_default()`
|
774
|
-
|
775
|
-
: The ruby value that a clear object should return.
|
776
|
-
|
777
|
-
If you wish to access parameters from inside these methods, you can
|
778
|
-
use `eval_parameter(key)`.
|
779
|
-
|
780
|
-
Here is an example of a big integer implementation.
|
781
|
-
|
782
|
-
# A custom big integer format. Binary format is:
|
783
|
-
# 1 byte : 0 for positive, non zero for negative
|
784
|
-
# x bytes : Little endian stream of 7 bit bytes representing the
|
785
|
-
# positive form of the integer. The upper bit of each byte
|
786
|
-
# is set when there are more bytes in the stream.
|
787
|
-
class BigInteger < BinData::BasePrimitive
|
788
|
-
|
789
|
-
def value_to_binary_string(value)
|
790
|
-
negative = (value < 0) ? 1 : 0
|
791
|
-
value = value.abs
|
792
|
-
bytes = [negative]
|
793
|
-
loop do
|
794
|
-
seven_bit_byte = value & 0x7f
|
795
|
-
value >>= 7
|
796
|
-
has_more = value.nonzero? ? 0x80 : 0
|
797
|
-
byte = has_more | seven_bit_byte
|
798
|
-
bytes.push(byte)
|
799
|
-
|
800
|
-
break if has_more.zero?
|
801
|
-
end
|
802
|
-
|
803
|
-
bytes.collect { |b| b.chr }.join
|
804
|
-
end
|
805
|
-
|
806
|
-
def read_and_return_value(io)
|
807
|
-
negative = read_uint8(io).nonzero?
|
808
|
-
value = 0
|
809
|
-
bit_shift = 0
|
810
|
-
loop do
|
811
|
-
byte = read_uint8(io)
|
812
|
-
has_more = byte & 0x80
|
813
|
-
seven_bit_byte = byte & 0x7f
|
814
|
-
value |= seven_bit_byte << bit_shift
|
815
|
-
bit_shift += 7
|
816
|
-
|
817
|
-
break if has_more.zero?
|
818
|
-
end
|
819
|
-
|
820
|
-
negative ? -value : value
|
821
|
-
end
|
822
|
-
|
823
|
-
def sensible_default
|
824
|
-
0
|
825
|
-
end
|
826
|
-
|
827
|
-
def read_uint8(io)
|
828
|
-
io.readbytes(1).unpack("C").at(0)
|
829
|
-
end
|
830
|
-
end
|
831
|
-
{:ruby}
|
832
|
-
|
833
|
-
---------------------------------------------------------------------------
|
834
|
-
|
835
|
-
# Compound Types
|
836
|
-
|
837
|
-
Compound types contain more that a single value. These types are
|
838
|
-
Records, Arrays and Choices.
|
839
|
-
|
840
|
-
## Arrays
|
841
|
-
|
842
|
-
A BinData array is a list of data objects of the same type. It behaves
|
843
|
-
much the same as the standard Ruby array, supporting most of the common
|
844
|
-
methods.
|
845
|
-
|
846
|
-
### Array syntax
|
847
|
-
|
848
|
-
When instantiating an array, the type of object it contains must be
|
849
|
-
specified. The two different ways of declaring this are the `:type`
|
850
|
-
parameter and the block form.
|
851
|
-
|
852
|
-
class A < BinData::Record
|
853
|
-
array :numbers, :type => :uint8, :initial_length => 3
|
854
|
-
end
|
855
|
-
-vs-
|
856
|
-
|
857
|
-
class A < BinData::Record
|
858
|
-
array :numbers, :initial_length => 3 do
|
859
|
-
uint8
|
860
|
-
end
|
861
|
-
end
|
862
|
-
{:ruby}
|
863
|
-
|
864
|
-
For the simple case, the `:type` parameter is usually clearer. When the
|
865
|
-
array type has parameters, the block form becomes easier to read.
|
866
|
-
|
867
|
-
class A < BinData::Record
|
868
|
-
array :numbers, :type => [:uint8, {:initial_value => :index}],
|
869
|
-
:initial_length => 3
|
870
|
-
end
|
871
|
-
-vs-
|
872
|
-
|
873
|
-
class A < BinData::Record
|
874
|
-
array :numbers, :initial_length => 3 do
|
875
|
-
uint8 :initial_value => :index
|
876
|
-
end
|
877
|
-
end
|
878
|
-
{:ruby}
|
879
|
-
|
880
|
-
An array can also be declared as a custom type by moving the contents of
|
881
|
-
the block into a custom class. The above example could alternatively be
|
882
|
-
declared as:
|
883
|
-
|
884
|
-
class NumberArray < BinData::Array
|
885
|
-
uint8 :initial_value => :index
|
886
|
-
end
|
887
|
-
|
888
|
-
class A < BinData::Record
|
889
|
-
number_array :numbers, :initial_length => 3
|
890
|
-
end
|
891
|
-
{:ruby}
|
892
|
-
|
893
|
-
|
894
|
-
If the block form has multiple types declared, they are interpreted as
|
895
|
-
the contents of an [anonymous `struct`](#nested_records). To illustrate
|
896
|
-
this, consider the following representation of a polygon.
|
897
|
-
|
898
|
-
class Polygon < BinData::Record
|
899
|
-
endian :little
|
900
|
-
uint8 :num_points, :value => lambda { points.length }
|
901
|
-
array :points, :initial_length => :num_points do
|
902
|
-
double :x
|
903
|
-
double :y
|
904
|
-
end
|
905
|
-
end
|
906
|
-
|
907
|
-
triangle = Polygon.new
|
908
|
-
triangle.points[0].assign(:x => 1, :y => 2)
|
909
|
-
triangle.points[1].x = 3
|
910
|
-
triangle.points[1].y = 4
|
911
|
-
triangle.points << {:x => 5, :y => 6}
|
912
|
-
{:ruby}
|
913
|
-
|
914
|
-
### Array parameters
|
915
|
-
|
916
|
-
There are two different parameters that specify the length of the array.
|
917
|
-
|
918
|
-
`:initial_length`
|
919
|
-
|
920
|
-
: Specifies the initial length of a newly instantiated array.
|
921
|
-
The array may grow as elements are inserted.
|
922
|
-
|
923
|
-
obj = BinData::Array.new(:type => :int8, :initial_length => 4)
|
924
|
-
obj.read("\002\003\004\005\006\007")
|
925
|
-
obj.snapshot #=> [2, 3, 4, 5]
|
926
|
-
{:ruby}
|
927
|
-
|
928
|
-
`:read_until`
|
929
|
-
|
930
|
-
: While reading, elements are read until this condition is true. This
|
931
|
-
is typically used to read an array until a sentinel value is found.
|
932
|
-
The variables `index`, `element` and `array` are made available to
|
933
|
-
any lambda assigned to this parameter. If the value of this
|
934
|
-
parameter is the symbol `:eof`, then the array will read as much
|
935
|
-
data from the stream as possible.
|
936
|
-
|
937
|
-
obj = BinData::Array.new(:type => :int8,
|
938
|
-
:read_until => lambda { index == 1 })
|
939
|
-
obj.read("\002\003\004\005\006\007")
|
940
|
-
obj.snapshot #=> [2, 3]
|
941
|
-
|
942
|
-
obj = BinData::Array.new(:type => :int8,
|
943
|
-
:read_until => lambda { element >= 3.5 })
|
944
|
-
obj.read("\002\003\004\005\006\007")
|
945
|
-
obj.snapshot #=> [2, 3, 4]
|
946
|
-
|
947
|
-
obj = BinData::Array.new(:type => :int8,
|
948
|
-
:read_until => lambda { array[index] + array[index - 1] == 9 })
|
949
|
-
obj.read("\002\003\004\005\006\007")
|
950
|
-
obj.snapshot #=> [2, 3, 4, 5]
|
951
|
-
|
952
|
-
obj = BinData::Array.new(:type => :int8, :read_until => :eof)
|
953
|
-
obj.read("\002\003\004\005\006\007")
|
954
|
-
obj.snapshot #=> [2, 3, 4, 5, 6, 7]
|
955
|
-
{:ruby}
|
956
|
-
|
957
|
-
## Choices
|
958
|
-
|
959
|
-
A Choice is a collection of data objects of which only one is active at
|
960
|
-
any particular time. Method calls will be delegated to the active
|
961
|
-
choice. The possible types of objects that a choice contains is
|
962
|
-
controlled by the `:choices` parameter, while the `:selection` parameter
|
963
|
-
specifies the active choice.
|
964
|
-
|
965
|
-
### Choice syntax
|
966
|
-
|
967
|
-
Choices have two ways of specifying the possible data objects they can
|
968
|
-
contain. The `:choices` parameter or the block form. The block form is
|
969
|
-
usually clearer and is prefered.
|
970
|
-
|
971
|
-
class MyInt16 < BinData::Record
|
972
|
-
uint8 :e, :check_value => lambda { value == 0 or value == 1 }
|
973
|
-
choice :int, :selection => :e,
|
974
|
-
:choices => {0 => :int16be, 1 => :int16le}
|
975
|
-
end
|
976
|
-
-vs-
|
977
|
-
|
978
|
-
class MyInt16 < BinData::Record
|
979
|
-
uint8 :e, :check_value => lambda { value == 0 or value == 1 }
|
980
|
-
choice :int, :selection => :e do
|
981
|
-
int16be 0
|
982
|
-
int16le 1
|
983
|
-
end
|
984
|
-
end
|
985
|
-
{:ruby}
|
986
|
-
|
987
|
-
Like all compound types, a choice can be declared as its own type. The
|
988
|
-
above example can be declared as:
|
989
|
-
|
990
|
-
class BigLittleInt16 < BinData::Choice
|
991
|
-
int16be 0
|
992
|
-
int16le 1
|
993
|
-
end
|
994
|
-
|
995
|
-
class MyInt16 < BinData::Record
|
996
|
-
uint8 :e, :check_value => lambda { value == 0 or value == 1 }
|
997
|
-
bit_little_int_16 :int, :selection => :e
|
998
|
-
end
|
999
|
-
{:ruby}
|
1000
|
-
|
1001
|
-
The general form of the choice is
|
1002
|
-
|
1003
|
-
class MyRecord < BinData::Record
|
1004
|
-
choice :name, :selection => lambda { ... } do
|
1005
|
-
type key, :param1 => "foo", :param2 => "bar" ... # option 1
|
1006
|
-
type key, :param1 => "foo", :param2 => "bar" ... # option 2
|
1007
|
-
end
|
1008
|
-
end
|
1009
|
-
{:ruby}
|
1010
|
-
|
1011
|
-
`type`
|
1012
|
-
: is the name of a supplied type (e.g. `uint32be`, `string`)
|
1013
|
-
or a user defined type. This is the same as for Records.
|
1014
|
-
|
1015
|
-
`key`
|
1016
|
-
: is the value that `:selection` will return to specify that this
|
1017
|
-
choice is currently active. The key can be any ruby type (`String`,
|
1018
|
-
`Numeric` etc) except `Symbol`.
|
1019
|
-
|
1020
|
-
### Choice parameters
|
1021
|
-
|
1022
|
-
`:choices`
|
1023
|
-
|
1024
|
-
: Either an array or a hash specifying the possible data objects. The
|
1025
|
-
format of the array/hash.values is a list of symbols representing
|
1026
|
-
the data object type. If a choice is to have params passed to it,
|
1027
|
-
then it should be provided as `[type_symbol, hash_params]`. An
|
1028
|
-
implementation constraint is that the hash may not contain symbols
|
1029
|
-
as keys.
|
1030
|
-
|
1031
|
-
`:selection`
|
1032
|
-
|
1033
|
-
: An index/key into the `:choices` array/hash which specifies the
|
1034
|
-
currently active choice.
|
1035
|
-
|
1036
|
-
`:copy_on_change`
|
1037
|
-
|
1038
|
-
: If set to `true`, copy the value of the previous selection to the
|
1039
|
-
current selection whenever the selection changes. Default is
|
1040
|
-
`false`.
|
1041
|
-
|
1042
|
-
Examples
|
1043
|
-
|
1044
|
-
type1 = [:string, {:value => "Type1"}]
|
1045
|
-
type2 = [:string, {:value => "Type2"}]
|
1046
|
-
|
1047
|
-
choices = {5 => type1, 17 => type2}
|
1048
|
-
obj = BinData::Choice.new(:choices => choices, :selection => 5)
|
1049
|
-
obj # => "Type1"
|
1050
|
-
|
1051
|
-
choices = [ type1, type2 ]
|
1052
|
-
obj = BinData::Choice.new(:choices => choices, :selection => 1)
|
1053
|
-
obj # => "Type2"
|
1054
|
-
|
1055
|
-
class MyNumber < BinData::Record
|
1056
|
-
int8 :is_big_endian
|
1057
|
-
choice :data, :selection => lambda { is_big_endian != 0 },
|
1058
|
-
:copy_on_change => true do
|
1059
|
-
int32le false
|
1060
|
-
int32be true
|
1061
|
-
end
|
1062
|
-
end
|
1063
|
-
|
1064
|
-
obj = MyNumber.new
|
1065
|
-
obj.is_big_endian = 1
|
1066
|
-
obj.data = 5
|
1067
|
-
obj.to_binary_s #=> "\001\000\000\000\005"
|
1068
|
-
|
1069
|
-
obj.is_big_endian = 0
|
1070
|
-
obj.to_binary_s #=> "\000\005\000\000\000"
|
1071
|
-
{:ruby}
|
1072
|
-
|
1073
|
-
### Default selection
|
1074
|
-
|
1075
|
-
A key of `:default` can be specified as a default selection. If the value of the
|
1076
|
-
selection isn't specified then the :default will be used. The previous `MyNumber`
|
1077
|
-
example used a flag for endian. Zero is little endian while any other value
|
1078
|
-
is big endian. This can be concisely written as:
|
1079
|
-
|
1080
|
-
class MyNumber < BinData::Record
|
1081
|
-
int8 :is_big_endian
|
1082
|
-
choice :data, :selection => :is_big_endian,
|
1083
|
-
:copy_on_change => true do
|
1084
|
-
int32le 0 # zero is little endian
|
1085
|
-
int32be :default # anything else is big endian
|
1086
|
-
end
|
1087
|
-
end
|
1088
|
-
{:ruby}
|
1089
|
-
|
1090
|
-
---------------------------------------------------------------------------
|
1091
|
-
|
1092
|
-
# Common Operations
|
1093
|
-
|
1094
|
-
There are operations common to all BinData types, including user defined
|
1095
|
-
ones. These are summarised here.
|
1096
|
-
|
1097
|
-
## Reading and writing
|
1098
|
-
|
1099
|
-
`::read(io)`
|
1100
|
-
|
1101
|
-
: Creates a BinData object and reads its value from the given string
|
1102
|
-
or `IO`. The newly created object is returned.
|
1103
|
-
|
1104
|
-
obj = BinData::Int8.read("\xff")
|
1105
|
-
obj.snapshot #=> -1
|
1106
|
-
{:ruby}
|
1107
|
-
|
1108
|
-
`#read(io)`
|
1109
|
-
|
1110
|
-
: Reads and assigns binary data read from `io`.
|
1111
|
-
|
1112
|
-
obj = BinData::Stringz.new
|
1113
|
-
obj.read("string 1\0string 2\0")
|
1114
|
-
obj #=> "string 1"
|
1115
|
-
{:ruby}
|
1116
|
-
|
1117
|
-
`#write(io)`
|
1118
|
-
|
1119
|
-
: Writes the binary data representation of the object to `io`.
|
1120
|
-
|
1121
|
-
File.open("...", "wb") do |io|
|
1122
|
-
obj = BinData::Uint64be.new(568290145640170)
|
1123
|
-
obj.write(io)
|
1124
|
-
end
|
1125
|
-
{:ruby}
|
1126
|
-
|
1127
|
-
`#to_binary_s`
|
1128
|
-
|
1129
|
-
: Returns the binary data representation of this object as a string.
|
1130
|
-
|
1131
|
-
obj = BinData::Uint16be.new(4660)
|
1132
|
-
obj.to_binary_s #=> "\022\064"
|
1133
|
-
{:ruby}
|
1134
|
-
|
1135
|
-
## Manipulating
|
1136
|
-
|
1137
|
-
`#assign(value)`
|
1138
|
-
|
1139
|
-
: Assigns the given value to this object. `value` can be of the same
|
1140
|
-
format as produced by `#snapshot`, or it can be a compatible data
|
1141
|
-
object.
|
1142
|
-
|
1143
|
-
arr = BinData::Array.new(:type => :uint8)
|
1144
|
-
arr.assign([1, 2, 3, 4])
|
1145
|
-
arr.snapshot #=> [1, 2, 3, 4]
|
1146
|
-
{:ruby}
|
1147
|
-
|
1148
|
-
`#clear`
|
1149
|
-
|
1150
|
-
: Resets this object to its initial state.
|
1151
|
-
|
1152
|
-
obj = BinData::Int32be.new(:initial_value => 42)
|
1153
|
-
obj.assign(50)
|
1154
|
-
obj.clear
|
1155
|
-
obj #=> 42
|
1156
|
-
{:ruby}
|
1157
|
-
|
1158
|
-
`#clear?`
|
1159
|
-
|
1160
|
-
: Returns whether this object is in its initial state.
|
1161
|
-
|
1162
|
-
arr = BinData::Array.new(:type => :uint16be, :initial_length => 5)
|
1163
|
-
arr[3] = 42
|
1164
|
-
arr.clear? #=> false
|
1165
|
-
|
1166
|
-
arr[3].clear
|
1167
|
-
arr.clear? #=> true
|
1168
|
-
{:ruby}
|
1169
|
-
|
1170
|
-
## Inspecting
|
1171
|
-
|
1172
|
-
`#num_bytes`
|
1173
|
-
|
1174
|
-
: Returns the number of bytes required for the binary data
|
1175
|
-
representation of this object.
|
1176
|
-
|
1177
|
-
arr = BinData::Array.new(:type => :uint16be, :initial_length => 5)
|
1178
|
-
arr[0].num_bytes #=> 2
|
1179
|
-
arr.num_bytes #=> 10
|
1180
|
-
{:ruby}
|
1181
|
-
|
1182
|
-
`#snapshot`
|
1183
|
-
|
1184
|
-
: Returns the value of this object as primitive Ruby objects
|
1185
|
-
(numerics, strings, arrays and hashs). The output of `#snapshot`
|
1186
|
-
may be useful for serialization or as a reduced memory usage
|
1187
|
-
representation.
|
1188
|
-
|
1189
|
-
obj = BinData::Uint8.new(2)
|
1190
|
-
obj.class #=> BinData::Uint8
|
1191
|
-
obj + 3 #=> 5
|
1192
|
-
|
1193
|
-
obj.snapshot #=> 2
|
1194
|
-
obj.snapshot.class #=> Fixnum
|
1195
|
-
{:ruby}
|
1196
|
-
|
1197
|
-
`#offset`
|
1198
|
-
|
1199
|
-
: Returns the offset of this object with respect to the most distant
|
1200
|
-
ancestor structure it is contained within. This is most likely to
|
1201
|
-
be used with arrays and records.
|
1202
|
-
|
1203
|
-
class Tuple < BinData::Record
|
1204
|
-
int8 :a
|
1205
|
-
int8 :b
|
1206
|
-
end
|
1207
|
-
|
1208
|
-
arr = BinData::Array.new(:type => :tuple, :initial_length => 3)
|
1209
|
-
arr[2].b.offset #=> 5
|
1210
|
-
{:ruby}
|
1211
|
-
|
1212
|
-
`#rel_offset`
|
1213
|
-
|
1214
|
-
: Returns the offset of this object with respect to the parent
|
1215
|
-
structure it is contained within. Compare this to `#offset`.
|
1216
|
-
|
1217
|
-
class Tuple < BinData::Record
|
1218
|
-
int8 :a
|
1219
|
-
int8 :b
|
1220
|
-
end
|
1221
|
-
|
1222
|
-
arr = BinData::Array.new(:type => :tuple, :initial_length => 3)
|
1223
|
-
arr[2].b.rel_offset #=> 1
|
1224
|
-
{:ruby}
|
1225
|
-
|
1226
|
-
`#inspect`
|
1227
|
-
|
1228
|
-
: Returns a human readable representation of this object. This is a
|
1229
|
-
shortcut to #snapshot.inspect.
|
1230
|
-
|
1231
|
-
---------------------------------------------------------------------------
|
1232
|
-
|
1233
|
-
# Advanced Topics
|
1234
|
-
|
1235
|
-
## Debugging
|
1236
|
-
|
1237
|
-
BinData includes several features to make it easier to debug
|
1238
|
-
declarations.
|
1239
|
-
|
1240
|
-
### Tracing
|
1241
|
-
|
1242
|
-
BinData has the ability to trace the results of reading a data
|
1243
|
-
structure.
|
1244
|
-
|
1245
|
-
class A < BinData::Record
|
1246
|
-
int8 :a
|
1247
|
-
bit4 :b
|
1248
|
-
bit2 :c
|
1249
|
-
array :d, :initial_length => 6, :type => :bit1
|
1250
|
-
end
|
1251
|
-
|
1252
|
-
BinData::trace_reading do
|
1253
|
-
A.read("\373\225\220")
|
1254
|
-
end
|
1255
|
-
{:ruby}
|
1256
|
-
|
1257
|
-
Results in the following being written to `STDERR`.
|
1258
|
-
|
1259
|
-
obj.a => -5
|
1260
|
-
obj.b => 9
|
1261
|
-
obj.c => 1
|
1262
|
-
obj.d[0] => 0
|
1263
|
-
obj.d[1] => 1
|
1264
|
-
obj.d[2] => 1
|
1265
|
-
obj.d[3] => 0
|
1266
|
-
obj.d[4] => 0
|
1267
|
-
obj.d[5] => 1
|
1268
|
-
{:ruby}
|
1269
|
-
|
1270
|
-
### Rest
|
1271
|
-
|
1272
|
-
The rest keyword will consume the input stream from the current position
|
1273
|
-
to the end of the stream.
|
1274
|
-
|
1275
|
-
class A < BinData::Record
|
1276
|
-
string :a, :read_length => 5
|
1277
|
-
rest :rest
|
1278
|
-
end
|
1279
|
-
|
1280
|
-
obj = A.read("abcdefghij")
|
1281
|
-
obj.a #=> "abcde"
|
1282
|
-
obj.rest #=" "fghij"
|
1283
|
-
{:ruby}
|
1284
|
-
|
1285
|
-
### Hidden fields
|
1286
|
-
|
1287
|
-
The typical way to view the contents of a BinData record is to call
|
1288
|
-
`#snapshot` or `#inspect`. This gives all fields and their values. The
|
1289
|
-
`hide` keyword can be used to prevent certain fields from appearing in
|
1290
|
-
this output. This removes clutter and allows the developer to focus on
|
1291
|
-
what they are currently interested in.
|
1292
|
-
|
1293
|
-
class Testing < BinData::Record
|
1294
|
-
hide :a, :b
|
1295
|
-
string :a, :read_length => 10
|
1296
|
-
string :b, :read_length => 10
|
1297
|
-
string :c, :read_length => 10
|
1298
|
-
end
|
1299
|
-
|
1300
|
-
obj = Testing.read(("a" * 10) + ("b" * 10) + ("c" * 10))
|
1301
|
-
obj.snapshot #=> {"c"=>"cccccccccc"}
|
1302
|
-
obj.to_binary_s #=> "aaaaaaaaaabbbbbbbbbbcccccccccc"
|
1303
|
-
{:ruby}
|
1304
|
-
|
1305
|
-
## Parameterizing User Defined Types
|
1306
|
-
|
1307
|
-
All BinData types have parameters that allow the behaviour of an object
|
1308
|
-
to be specified at initialization time. User defined types may also
|
1309
|
-
specify parameters. There are two types of parameters: mandatory and
|
1310
|
-
default.
|
1311
|
-
|
1312
|
-
### Mandatory Parameters
|
1313
|
-
|
1314
|
-
Mandatory parameters must be specified when creating an instance of the
|
1315
|
-
type.
|
1316
|
-
|
1317
|
-
class Polygon < BinData::Record
|
1318
|
-
mandatory_parameter :num_vertices
|
1319
|
-
|
1320
|
-
uint8 :num, :value => lambda { vertices.length }
|
1321
|
-
array :vertices, :initial_length => :num_vertices do
|
1322
|
-
int8 :x
|
1323
|
-
int8 :y
|
1324
|
-
end
|
1325
|
-
end
|
1326
|
-
|
1327
|
-
triangle = Polygon.new
|
1328
|
-
#=> raises ArgumentError: parameter 'num_vertices' must be specified in Polygon
|
1329
|
-
|
1330
|
-
triangle = Polygon.new(:num_vertices => 3)
|
1331
|
-
triangle.snapshot #=> {"num" => 3, "vertices" =>
|
1332
|
-
[{"x"=>0, "y"=>0}, {"x"=>0, "y"=>0}, {"x"=>0, "y"=>0}]}
|
1333
|
-
{:ruby}
|
1334
|
-
|
1335
|
-
### Default Parameters
|
1336
|
-
|
1337
|
-
Default parameters are optional. These parameters have a default value
|
1338
|
-
that may be overridden when an instance of the type is created.
|
1339
|
-
|
1340
|
-
class Phrase < BinData::Primitive
|
1341
|
-
default_parameter :number => "three"
|
1342
|
-
default_parameter :adjective => "blind"
|
1343
|
-
default_parameter :noun => "mice"
|
1344
|
-
|
1345
|
-
stringz :a, :initial_value => :number
|
1346
|
-
stringz :b, :initial_value => :adjective
|
1347
|
-
stringz :c, :initial_value => :noun
|
1348
|
-
|
1349
|
-
def get; "#{a} #{b} #{c}"; end
|
1350
|
-
def set(v)
|
1351
|
-
if /(.*) (.*) (.*)/ =~ v
|
1352
|
-
self.a, self.b, self.c = $1, $2, $3
|
1353
|
-
end
|
1354
|
-
end
|
1355
|
-
end
|
1356
|
-
|
1357
|
-
obj = Phrase.new(:number => "two", :adjective => "deaf")
|
1358
|
-
obj.to_s #=> "two deaf mice"
|
1359
|
-
{:ruby}
|
1360
|
-
|
1361
|
-
## Extending existing Types
|
1362
|
-
|
1363
|
-
Sometimes you wish to create a new type that is simply an existing type
|
1364
|
-
with some predefined parameters. Examples could be an array with a
|
1365
|
-
specified type, or an integer with an initial value.
|
1366
|
-
|
1367
|
-
This can be achieved by subclassing the existing type and providing
|
1368
|
-
default parameters. These parameters can of course be overridden at
|
1369
|
-
initialisation time.
|
1370
|
-
|
1371
|
-
Here we define an array that contains big endian 16 bit integers. The
|
1372
|
-
array has a preferred initial length.
|
1373
|
-
|
1374
|
-
class IntArray < BinData::Array
|
1375
|
-
default_parameters :type => :uint16be, :initial_length => 5
|
1376
|
-
end
|
1377
|
-
|
1378
|
-
arr = IntArray.new
|
1379
|
-
arr.size #=> 5
|
1380
|
-
{:ruby}
|
1381
|
-
|
1382
|
-
The initial length can be overridden at initialisation time.
|
1383
|
-
|
1384
|
-
arr = IntArray.new(:initial_length => 8)
|
1385
|
-
arr.size #=> 8
|
1386
|
-
{:ruby}
|
1387
|
-
|
1388
|
-
We can also use the block form syntax:
|
1389
|
-
|
1390
|
-
class IntArray < BinData::Array
|
1391
|
-
endian :big
|
1392
|
-
default_parameter :initial_length => 5
|
1393
|
-
|
1394
|
-
uint16
|
1395
|
-
end
|
1396
|
-
{:ruby}
|
1397
|
-
|
1398
|
-
## Dynamically creating Types
|
1399
|
-
|
1400
|
-
Sometimes the format of a record is not known until runtime. You can use the
|
1401
|
-
`BinData::Struct` class to dynamically create a new type. To be able to reuse
|
1402
|
-
this type, you can give it a name.
|
1403
|
-
|
1404
|
-
# Dynamically create my_new_type
|
1405
|
-
BinData::Struct.new(:name => :my_new_type,
|
1406
|
-
:fields => [ [:int8, :a], [:int8, :b] ])
|
1407
|
-
|
1408
|
-
# Create an array of these types
|
1409
|
-
array = BinData::Array.new(:type => :my_new_type)
|
1410
|
-
{:ruby}
|
1411
|
-
|
1412
|
-
## Skipping over unused data
|
1413
|
-
|
1414
|
-
Some structures contain binary data that is irrelevant to your purposes.
|
1415
|
-
|
1416
|
-
Say you are interested in 50 bytes of data located 10 megabytes into the
|
1417
|
-
stream. One way of accessing this useful data is:
|
1418
|
-
|
1419
|
-
class MyData < BinData::Record
|
1420
|
-
string :length => 10 * 1024 * 1024
|
1421
|
-
string :data, :length => 50
|
1422
|
-
end
|
1423
|
-
{:ruby}
|
1424
|
-
|
1425
|
-
The advantage of this method is that the irrelevant data is preserved
|
1426
|
-
when writing the record. The disadvantage is that even if you don't care
|
1427
|
-
about preserving this irrelevant data, it still occupies memory.
|
1428
|
-
|
1429
|
-
If you don't need to preserve this data, an alternative is to use
|
1430
|
-
`skip` instead of `string`. When reading it will seek over the irrelevant
|
1431
|
-
data and won't consume space in memory. When writing it will write
|
1432
|
-
`:length` number of zero bytes.
|
1433
|
-
|
1434
|
-
class MyData < BinData::Record
|
1435
|
-
skip :length => 10 * 1024 * 1024
|
1436
|
-
string :data, :length => 50
|
1437
|
-
end
|
1438
|
-
{:ruby}
|
1439
|
-
|
1440
|
-
## Determining stream length
|
1441
|
-
|
1442
|
-
Some file formats don't use length fields but rather read until the end
|
1443
|
-
of the file. The stream length is needed when reading these formats. The
|
1444
|
-
`count_bytes_remaining` keyword will give the number of bytes remaining in the
|
1445
|
-
stream.
|
1446
|
-
|
1447
|
-
Consider a string followed by a 2 byte checksum. The length of the string is
|
1448
|
-
not specified but is implied by the file length.
|
1449
|
-
|
1450
|
-
class StringWithChecksum < BinData::Record
|
1451
|
-
count_bytes_remaining :bytes_remaining
|
1452
|
-
string :the_string, :read_length => lambda { bytes_remaining - 2 }
|
1453
|
-
int16le :checksum
|
1454
|
-
end
|
1455
|
-
{:ruby}
|
1456
|
-
|
1457
|
-
These file formats only work with seekable streams (e.g. files). These formats
|
1458
|
-
do not stream well as they must be buffered by the client before being
|
1459
|
-
processed. Consider using an explicit length when creating a new file format
|
1460
|
-
as it is easier to work with.
|
1461
|
-
|
1462
|
-
## Advanced Bitfields
|
1463
|
-
|
1464
|
-
Most types in a record are byte oriented. [Bitfields](#bit_based_integers)
|
1465
|
-
allow access to individual bits in an octet stream.
|
1466
|
-
|
1467
|
-
Sometimes a bitfield has unused elements such as
|
1468
|
-
|
1469
|
-
class RecordWithBitfield < BinData::Record
|
1470
|
-
bit1 :foo
|
1471
|
-
bit1 :bar
|
1472
|
-
bit1 :baz
|
1473
|
-
bit5 :unused
|
1474
|
-
|
1475
|
-
stringz :qux
|
1476
|
-
end
|
1477
|
-
{:ruby}
|
1478
|
-
|
1479
|
-
The problem with specifying an unused field is that the size of this
|
1480
|
-
field must be manually counted. This is a potential source of errors.
|
1481
|
-
|
1482
|
-
BinData provides a shortcut to skip to the next byte boundary with the
|
1483
|
-
`resume_byte_alignment` keyword.
|
1484
|
-
|
1485
|
-
class RecordWithBitfield < BinData::Record
|
1486
|
-
bit1 :foo
|
1487
|
-
bit1 :bar
|
1488
|
-
bit1 :baz
|
1489
|
-
resume_byte_alignment
|
1490
|
-
|
1491
|
-
stringz :qux
|
1492
|
-
end
|
1493
|
-
{:ruby}
|
1494
|
-
|
1495
|
-
Occasionally you will come across a format where primitive types (string
|
1496
|
-
and numerics) are not aligned on byte boundaries but are to be packed in
|
1497
|
-
the bit stream.
|
1498
|
-
|
1499
|
-
class PackedRecord < BinData::Record
|
1500
|
-
bit4 :a
|
1501
|
-
string :b, :length => 2 # note: byte-aligned
|
1502
|
-
bit1 :c
|
1503
|
-
int16le :d # note: byte-aligned
|
1504
|
-
bit3 :e
|
1505
|
-
end
|
1506
|
-
|
1507
|
-
obj = PackedRecord.read("\xff" * 10)
|
1508
|
-
obj.to_binary_s #=> "\360\377\377\200\377\377\340"
|
1509
|
-
{:ruby}
|
1510
|
-
|
1511
|
-
The above declaration does not work as expected because BinData's
|
1512
|
-
internal strings and integers are byte-aligned. We need bit-aligned
|
1513
|
-
versions of `string` and `int16le`.
|
1514
|
-
|
1515
|
-
class BitString < BinData::String
|
1516
|
-
bit_aligned
|
1517
|
-
end
|
1518
|
-
|
1519
|
-
class BitInt16le < BinData::Int16le
|
1520
|
-
bit_aligned
|
1521
|
-
end
|
1522
|
-
|
1523
|
-
class PackedRecord < BinData::Record
|
1524
|
-
bit4 :a
|
1525
|
-
bit_string :b, :length => 2
|
1526
|
-
bit1 :c
|
1527
|
-
bit_int16le :d
|
1528
|
-
bit3 :e
|
1529
|
-
end
|
1530
|
-
|
1531
|
-
obj = PackedRecord.read("\xff" * 10)
|
1532
|
-
obj.to_binary_s #=> "\377\377\377\377\377"
|
1533
|
-
{:ruby}
|
1534
|
-
|
1535
|
-
---------------------------------------------------------------------------
|
1536
|
-
|
1537
|
-
# FAQ
|
1538
|
-
|
1539
|
-
## I'm using Ruby 1.9. How do I use string encodings with BinData?
|
1540
|
-
|
1541
|
-
BinData will internally use 8bit binary strings to represent the data.
|
1542
|
-
You do not need to worry about converting between encodings.
|
1543
|
-
|
1544
|
-
If you wish BinData to present string data in a specific encoding, you
|
1545
|
-
can override `#snapshot` as illustrated below:
|
1546
|
-
|
1547
|
-
class UTF8String < BinData::String
|
1548
|
-
def snapshot
|
1549
|
-
super.force_encoding('UTF-8')
|
1550
|
-
end
|
1551
|
-
end
|
1552
|
-
|
1553
|
-
str = UTF8String.new("\xC3\x85\xC3\x84\xC3\x96")
|
1554
|
-
str #=> "ÅÄÖ"
|
1555
|
-
str.to_binary_s #=> "\xC3\x85\xC3\x84\xC3\x96"
|
1556
|
-
{:ruby}
|
1557
|
-
|
1558
|
-
## How do I speed up initialization?
|
1559
|
-
|
1560
|
-
I'm doing this and it's slow.
|
1561
|
-
|
1562
|
-
999.times do |i|
|
1563
|
-
foo = Foo.new(:bar => "baz")
|
1564
|
-
...
|
1565
|
-
end
|
1566
|
-
{:ruby}
|
1567
|
-
|
1568
|
-
BinData is optimized to be declarative. For imperative use, the
|
1569
|
-
above naïve approach will be slow. Below are faster alternatives.
|
1570
|
-
|
1571
|
-
The fastest approach is to reuse objects by calling `#clear` instead of
|
1572
|
-
instantiating more objects.
|
1573
|
-
|
1574
|
-
foo = Foo.new(:bar => "baz")
|
1575
|
-
999.times do
|
1576
|
-
foo.clear
|
1577
|
-
...
|
1578
|
-
end
|
1579
|
-
{:ruby}
|
1580
|
-
|
1581
|
-
If you can't reuse objects, then consider the prototype pattern.
|
1582
|
-
|
1583
|
-
prototype = Foo.new(:bar => "baz")
|
1584
|
-
999.times do
|
1585
|
-
foo = prototype.new
|
1586
|
-
...
|
1587
|
-
end
|
1588
|
-
{:ruby}
|
1589
|
-
|
1590
|
-
The prefered approach is to be declarative.
|
1591
|
-
|
1592
|
-
class FooList < BinData::Array
|
1593
|
-
default_parameter :initial_length => 999
|
1594
|
-
|
1595
|
-
foo :bar => "baz"
|
1596
|
-
end
|
1597
|
-
|
1598
|
-
array = FooList.new
|
1599
|
-
array.each { ... }
|
1600
|
-
{:ruby}
|
1601
|
-
|
1602
|
-
## How do I model this complex nested format?
|
1603
|
-
|
1604
|
-
A common pattern in file formats and network protocols is
|
1605
|
-
[type-length-value](http://en.wikipedia.org/wiki/Type-length-value). The
|
1606
|
-
`type` field specifies how to interpret the `value`. This gives a way to
|
1607
|
-
dynamically structure the data format. An example is the TCP/IP protocol
|
1608
|
-
suite. An IP datagram can contain a nested TCP, UDP or other packet type as
|
1609
|
-
decided by the `protocol` field.
|
1610
|
-
|
1611
|
-
Modelling this structure can be difficult when the nesting is recursive, e.g.
|
1612
|
-
IP tunneling. Here is an example of the simplest possible recursive TLV structure,
|
1613
|
-
a [list that can contains atoms or other
|
1614
|
-
lists](http://github.com/dmendel/bindata/blob/master/examples/list.rb).
|
1615
|
-
|
1616
|
-
---------------------------------------------------------------------------
|
1617
|
-
|
1618
|
-
# Alternatives
|
1619
|
-
|
1620
|
-
This section is purely historic. All the alternatives to BinData are
|
1621
|
-
no longer actively maintained.
|
1622
|
-
|
1623
|
-
There are several alternatives to BinData. Below is a comparison
|
1624
|
-
between BinData and its alternatives.
|
1625
|
-
|
1626
|
-
The short form is that BinData is the best choice for most cases.
|
1627
|
-
It is the most full featured of all the alternatives. It is also
|
1628
|
-
arguably the most readable and easiest way to parse and write
|
1629
|
-
binary data.
|
1630
|
-
|
1631
|
-
### [BitStruct](http://rubyforge.org/projects/bit-struct)
|
1632
|
-
|
1633
|
-
BitStruct is the most complete of all the alternatives. It is
|
1634
|
-
declarative and supports most of the same primitive types as BinData.
|
1635
|
-
Its special feature is a self documenting feature for report generation.
|
1636
|
-
BitStruct's design choice is to favour speed over flexibility.
|
1637
|
-
|
1638
|
-
The major limitation of BitStruct is that it does not support variable
|
1639
|
-
length fields and dependent fields. This makes it difficult to work
|
1640
|
-
with any non trivial file formats.
|
1641
|
-
|
1642
|
-
If speed is important and you are only dealing with simple binary data
|
1643
|
-
types then BitStruct might be a good choice. For non trivial data
|
1644
|
-
types, BinData is the better choice.
|
1645
|
-
|
1646
|
-
### [BinaryParse](http://rubyforge.org/projects/binaryparse)
|
1647
|
-
|
1648
|
-
BinaryParse is a declarative style packer / unpacker. It provides the
|
1649
|
-
same primitives as Ruby's `#pack`, with the addition of date and time.
|
1650
|
-
Like BitStruct, it doesn't provide dependent or variable length fields.
|
1651
|
-
|
1652
|
-
### [BinStruct](http://rubyforge.org/projects/metafuzz)
|
1653
|
-
|
1654
|
-
BinStruct is an imperative approach to unpacking binary data. It does
|
1655
|
-
provide some declarative style syntax sugar. It provides support for
|
1656
|
-
the most common primitive types, as well as arbitrary length bitfields.
|
1657
|
-
|
1658
|
-
Its main focus is as a binary fuzzer, rather than as a generic decoding
|
1659
|
-
/ encoding library.
|
1660
|
-
|
1661
|
-
### [Packable](http://github.com/marcandre/packable/tree/master)
|
1662
|
-
|
1663
|
-
Packable makes it much nicer to use Ruby's `#pack` and `#unpack`
|
1664
|
-
methods. Instead of having to remember that, for example `"n"` is the
|
1665
|
-
code to pack a 16 bit big endian integer, packable provides many
|
1666
|
-
convenient shortcuts. In the case of `"n"`, `{:bytes => 2, :endian => :big}`
|
1667
|
-
may be used instead.
|
1668
|
-
|
1669
|
-
Using Packable improves the readability of `#pack` and `#unpack`
|
1670
|
-
methods, but explicitly calls to `#pack` and `#unpack` aren't as
|
1671
|
-
readable as a declarative approach.
|
1672
|
-
|
1673
|
-
### [Bitpack](http://rubyforge.org/projects/bitpack)
|
1674
|
-
|
1675
|
-
Bitpack provides methods to extract big endian integers of arbitrary bit
|
1676
|
-
length from an octet stream.
|
1677
|
-
|
1678
|
-
The extraction code is written in `C`, so if speed is important and bit
|
1679
|
-
manipulation is all the functionality you require then this may be an
|
1680
|
-
alternative.
|
1681
|
-
|
1682
|
-
---------------------------------------------------------------------------
|