bindata 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bindata might be problematic. Click here for more details.

data/manual.md CHANGED
@@ -40,6 +40,8 @@ manipulating.
40
40
  It supports all the common datatypes that are found in structured binary
41
41
  data. Support for dependent and variable length fields is built in.
42
42
 
43
+ Last updated: 2011-06-14
44
+
43
45
  ## License
44
46
 
45
47
  BinData is released under the same license as Ruby.
@@ -698,9 +700,11 @@ Three of the fields have parameters.
698
700
  ## Strings
699
701
 
700
702
  BinData supports two types of strings - fixed size and zero terminated.
701
- Strings are treated as a sequence of 8bit bytes. This is the same as
702
- strings in Ruby 1.8. The issue of character encoding is ignored by
703
- BinData.
703
+ Strings are treated internally as a sequence of 8bit bytes. This is the
704
+ same as strings in Ruby 1.8. BinData fully supports Ruby 1.9 string
705
+ encodings. See this [FAQ
706
+ entry](#im_using_ruby_19_how_do_i_use_string_encodings_with_bindata) for
707
+ details.
704
708
 
705
709
  ### Fixed Sized Strings
706
710
 
@@ -834,6 +838,9 @@ and implement the following three methods:
834
838
 
835
839
  : The ruby value that a clear object should return.
836
840
 
841
+ If you wish to access parameters from inside these methods, you can
842
+ use `eval_parameter(key)`.
843
+
837
844
  Here is an example of a big integer implementation.
838
845
 
839
846
  # A custom big integer format. Binary format is:
@@ -842,7 +849,6 @@ Here is an example of a big integer implementation.
842
849
  # positive form of the integer. The upper bit of each byte
843
850
  # is set when there are more bytes in the stream.
844
851
  class BigInteger < BinData::BasePrimitive
845
- register_self
846
852
 
847
853
  def value_to_binary_string(value)
848
854
  negative = (value < 0) ? 1 : 0
@@ -1105,6 +1111,169 @@ Examples
1105
1111
 
1106
1112
  # Advanced Topics
1107
1113
 
1114
+ ## Debugging
1115
+
1116
+ BinData includes several features to make it easier to debug
1117
+ declarations.
1118
+
1119
+ ### Tracing
1120
+
1121
+ BinData has the ability to trace the results of reading a data
1122
+ structure.
1123
+
1124
+ class A < BinData::Record
1125
+ int8 :a
1126
+ bit4 :b
1127
+ bit2 :c
1128
+ array :d, :initial_length => 6, :type => :bit1
1129
+ end
1130
+
1131
+ BinData::trace_reading do
1132
+ A.read("\373\225\220")
1133
+ end
1134
+ {:ruby}
1135
+
1136
+ Results in the following being written to `STDERR`.
1137
+
1138
+ obj.a => -5
1139
+ obj.b => 9
1140
+ obj.c => 1
1141
+ obj.d[0] => 0
1142
+ obj.d[1] => 1
1143
+ obj.d[2] => 1
1144
+ obj.d[3] => 0
1145
+ obj.d[4] => 0
1146
+ obj.d[5] => 1
1147
+ {:ruby}
1148
+
1149
+ ### Rest
1150
+
1151
+ The rest keyword will consume the input stream from the current position
1152
+ to the end of the stream.
1153
+
1154
+ class A < BinData::Record
1155
+ string :a, :read_length => 5
1156
+ rest :rest
1157
+ end
1158
+
1159
+ obj = A.read("abcdefghij")
1160
+ obj.a #=> "abcde"
1161
+ obj.rest #=" "fghij"
1162
+ {:ruby}
1163
+
1164
+ ### Hidden fields
1165
+
1166
+ The typical way to view the contents of a BinData record is to call
1167
+ `#snapshot` or `#inspect`. This gives all fields and their values. The
1168
+ `hide` keyword can be used to prevent certain fields from appearing in
1169
+ this output. This removes clutter and allows the developer to focus on
1170
+ what they are currently interested in.
1171
+
1172
+ class Testing < BinData::Record
1173
+ hide :a, :b
1174
+ string :a, :read_length => 10
1175
+ string :b, :read_length => 10
1176
+ string :c, :read_length => 10
1177
+ end
1178
+
1179
+ obj = Testing.read(("a" * 10) + ("b" * 10) + ("c" * 10))
1180
+ obj.snapshot #=> {"c"=>"cccccccccc"}
1181
+ obj.to_binary_s #=> "aaaaaaaaaabbbbbbbbbbcccccccccc"
1182
+ {:ruby}
1183
+
1184
+ ## Parameterizing User Defined Types
1185
+
1186
+ All BinData types have parameters that allow the behaviour of an object
1187
+ to be specified at initialization time. User defined types may also
1188
+ specify parameters. There are two types of parameters: mandatory and
1189
+ default.
1190
+
1191
+ ### Mandatory Parameters
1192
+
1193
+ Mandatory parameters must be specified when creating an instance of the
1194
+ type.
1195
+
1196
+ class Polygon < BinData::Record
1197
+ mandatory_parameter :num_edges
1198
+
1199
+ uint8 :num, :value => lambda { vertices.length }
1200
+ array :vertices, :initial_length => :num_edges do
1201
+ int8 :x
1202
+ int8 :y
1203
+ end
1204
+ end
1205
+
1206
+ triangle = Polygon.new
1207
+ #=> raises ArgumentError: parameter 'num_edges' must be specified in Polygon
1208
+
1209
+ triangle = Polygon.new(:num_edges => 3)
1210
+ triangle.snapshot #=> {"num" => 3, "vertices" =>
1211
+ [{"x"=>0, "y"=>0}, {"x"=>0, "y"=>0}, {"x"=>0, "y"=>0}]}
1212
+ {:ruby}
1213
+
1214
+ ### Default Parameters
1215
+
1216
+ Default parameters are optional. These parameters have a default value
1217
+ that may be overridden when an instance of the type is created.
1218
+
1219
+ class Phrase < BinData::Primitive
1220
+ default_parameter :number => "three"
1221
+ default_parameter :adjective => "blind"
1222
+ default_parameter :noun => "mice"
1223
+
1224
+ stringz :a, :initial_value => :number
1225
+ stringz :b, :initial_value => :adjective
1226
+ stringz :c, :initial_value => :noun
1227
+
1228
+ def get; "#{a} #{b} #{c}"; end
1229
+ def set(v)
1230
+ if /(.*) (.*) (.*)/ =~ v
1231
+ self.a, self.b, self.c = $1, $2, $3
1232
+ end
1233
+ end
1234
+ end
1235
+
1236
+ obj = Phrase.new(:number => "two", :adjective => "deaf")
1237
+ obj.to_s #=> "two deaf mice"
1238
+ {:ruby}
1239
+
1240
+ ## Extending existing Types
1241
+
1242
+ Sometimes you wish to create a new type that is simply an existing type
1243
+ with some predefined parameters. Examples could be an array with a
1244
+ specified type, or an integer with an initial value.
1245
+
1246
+ This can be achieved by subclassing the existing type and providing
1247
+ default parameters. These parameters can of course be overridden at
1248
+ initialisation time.
1249
+
1250
+ Here we define an array that contains big endian 16 bit integers. The
1251
+ array has a preferred initial length.
1252
+
1253
+ class IntArray < BinData::Array
1254
+ default_parameters :type => :uint16be, :initial_length => 5
1255
+ end
1256
+
1257
+ arr = IntArray.new
1258
+ arr.size #=> 5
1259
+ {:ruby}
1260
+
1261
+ The initial length can be overridden at initialisation time.
1262
+
1263
+ arr = IntArray.new(:initial_length => 8)
1264
+ arr.size #=> 8
1265
+ {:ruby}
1266
+
1267
+ We can also use the block form syntax:
1268
+
1269
+ class IntArray < BinData::Array
1270
+ endian :big
1271
+ default_parameter :initial_length => 5
1272
+
1273
+ uint16
1274
+ end
1275
+ {:ruby}
1276
+
1108
1277
  ## Skipping over unused data
1109
1278
 
1110
1279
  Some structures contain binary data that is irrelevant to your purposes.
@@ -1176,155 +1345,86 @@ versions of `string` and `int16le`.
1176
1345
  c.to_binary_s #=> "\377\377\377\377\377"
1177
1346
  {:ruby}
1178
1347
 
1179
- ## Wrappers
1180
-
1181
- Sometimes you wish to create a new type that is simply an existing type
1182
- with some predefined parameters. Examples could be an array with a
1183
- specified type, or an integer with an initial value.
1184
-
1185
- This can be achieved with a wrapper. A wrapper creates a new type based
1186
- on an existing type which has predefined parameters. These parameters
1187
- can of course be overridden at initialisation time.
1188
-
1189
- Here we define an array that contains big endian 16 bit integers. The
1190
- array has a preferred initial length.
1191
-
1192
- class IntArray < BinData::Wrapper
1193
- endian :big
1194
- array :type => :uint16, :initial_length => 5
1195
- end
1196
-
1197
- arr = IntArray.new
1198
- arr.size #=> 5
1199
- {:ruby}
1200
-
1201
- The initial length can be overridden at initialisation time.
1202
-
1203
- arr = IntArray.new(:initial_length => 8)
1204
- arr.size #=> 8
1205
- {:ruby}
1206
-
1207
- ## Parameterizing User Defined Types
1348
+ ---------------------------------------------------------------------------
1208
1349
 
1209
- All BinData types have parameters that allow the behaviour of an object
1210
- to be specified at initialization time. User defined types may also
1211
- specify parameters. There are two types of parameters: mandatory and
1212
- default.
1350
+ # FAQ
1213
1351
 
1214
- ### Mandatory Parameters
1352
+ ## I'm using Ruby 1.9. How do I use string encodings with BinData?
1215
1353
 
1216
- Mandatory parameters must be specified when creating an instance of the
1217
- type. The `:type` parameter of `Array` is an example of a mandatory
1218
- type.
1354
+ BinData will internally use 8bit binary strings to represent the data.
1355
+ You do not need to worry about converting between encodings.
1219
1356
 
1220
- class IntArray < BinData::Wrapper
1221
- mandatory_parameter :byte_count
1357
+ If you wish BinData to present string data in a specific encoding, you
1358
+ can override `#snapshot` as illustrated below:
1222
1359
 
1223
- array :type => :uint16be, :initial_length => lambda { byte_count / 2 }
1360
+ class UTF8String < BinData::String
1361
+ def snapshot
1362
+ super.force_encoding('UTF-8')
1363
+ end
1224
1364
  end
1225
1365
 
1226
- arr = IntArray.new
1227
- #=> raises ArgumentError: parameter 'byte_count' must be specified in IntArray
1228
-
1229
- arr = IntArray.new(:byte_count => 12)
1230
- arr.snapshot #=> [0, 0, 0, 0, 0, 0]
1366
+ str = UTF8String.new("\xC3\x85\xC3\x84\xC3\x96")
1367
+ str #=> "ÅÄÖ"
1368
+ str.to_binary_s #=> "\xC3\x85\xC3\x84\xC3\x96"
1231
1369
  {:ruby}
1232
1370
 
1233
- ### Default Parameters
1234
-
1235
- Default parameters are optional. These parameters have a default value
1236
- that may be overridden when an instance of the type is created.
1237
-
1238
- class Phrase < BinData::Primitive
1239
- default_parameter :number => "three"
1240
- default_parameter :adjective => "blind"
1241
- default_parameter :noun => "mice"
1371
+ ## How do I speed up initialization?
1242
1372
 
1243
- stringz :a, :initial_value => :number
1244
- stringz :b, :initial_value => :adjective
1245
- stringz :c, :initial_value => :noun
1373
+ I'm doing this and it's slow.
1246
1374
 
1247
- def get; "#{a} #{b} #{c}"; end
1248
- def set(v)
1249
- if /(.*) (.*) (.*)/ =~ v
1250
- self.a, self.b, self.c = $1, $2, $3
1251
- end
1252
- end
1375
+ 999.times do |i|
1376
+ foo = Foo.new(:bar => "baz")
1377
+ ...
1253
1378
  end
1254
-
1255
- obj = Phrase.new(:number => "two", :adjective => "deaf")
1256
- obj.to_s #=> "two deaf mice"
1257
1379
  {:ruby}
1258
1380
 
1259
- ## Debugging
1260
-
1261
- BinData includes several features to make it easier to debug
1262
- declarations.
1263
-
1264
- ### Tracing
1265
-
1266
- BinData has the ability to trace the results of reading a data
1267
- structure.
1381
+ BinData is optimized to be declarative. For imperative use, the
1382
+ above naïve approach will be slow. Below are faster alternatives.
1268
1383
 
1269
- class A < BinData::Record
1270
- int8 :a
1271
- bit4 :b
1272
- bit2 :c
1273
- array :d, :initial_length => 6, :type => :bit1
1274
- end
1384
+ The fastest approach is to reuse objects by calling `#clear` instead of
1385
+ instantiating more objects.
1275
1386
 
1276
- BinData::trace_reading do
1277
- A.read("\373\225\220")
1387
+ foo = Foo.new(:bar => "baz")
1388
+ 999.times do
1389
+ foo.clear
1390
+ ...
1278
1391
  end
1279
1392
  {:ruby}
1280
1393
 
1281
- Results in the following being written to `STDERR`.
1394
+ If you can't reuse objects, then consider the prototype pattern.
1282
1395
 
1283
- obj.a => -5
1284
- obj.b => 9
1285
- obj.c => 1
1286
- obj.d[0] => 0
1287
- obj.d[1] => 1
1288
- obj.d[2] => 1
1289
- obj.d[3] => 0
1290
- obj.d[4] => 0
1291
- obj.d[5] => 1
1396
+ prototype = Foo.new(:bar => "baz")
1397
+ 999.times do
1398
+ foo = prototype.new
1399
+ ...
1400
+ end
1292
1401
  {:ruby}
1293
1402
 
1294
- ### Rest
1403
+ The prefered approach is to be declarative.
1295
1404
 
1296
- The rest keyword will consume the input stream from the current position
1297
- to the end of the stream.
1405
+ class FooList < BinData::Array
1406
+ default_parameter :initial_length => 999
1298
1407
 
1299
- class A < BinData::Record
1300
- string :a, :read_length => 5
1301
- rest :rest
1408
+ foo :bar => "baz"
1302
1409
  end
1303
1410
 
1304
- obj = A.read("abcdefghij")
1305
- obj.a #=> "abcde"
1306
- obj.rest #=" "fghij"
1411
+ array = FooList.new
1412
+ array.each { ... }
1307
1413
  {:ruby}
1308
1414
 
1309
- ### Hidden fields
1310
-
1311
- The typical way to view the contents of a BinData record is to call
1312
- `#snapshot` or `#inspect`. This gives all fields and their values. The
1313
- `hide` keyword can be used to prevent certain fields from appearing in
1314
- this output. This removes clutter and allows the developer to focus on
1315
- what they are currently interested in.
1415
+ ## How do I model this complex nested format?
1316
1416
 
1317
- class Testing < BinData::Record
1318
- hide :a, :b
1319
- string :a, :read_length => 10
1320
- string :b, :read_length => 10
1321
- string :c, :read_length => 10
1322
- end
1417
+ A common pattern in file formats and network protocols is
1418
+ [type-length-value](http://en.wikipedia.org/wiki/Type-length-value). The
1419
+ `type` field specifies how to interpret the `value`. This gives a way to
1420
+ dynamically structure the data format. An example is the TCP/IP protocol
1421
+ suite. An IP datagram can contain a nested TCP, UDP or other packet type as
1422
+ decided by the `protocol` field.
1323
1423
 
1324
- obj = Testing.read(("a" * 10) + ("b" * 10) + ("c" * 10))
1325
- obj.snapshot #=> {"c"=>"cccccccccc"}
1326
- obj.to_binary_s #=> "aaaaaaaaaabbbbbbbbbbcccccccccc"
1327
- {:ruby}
1424
+ Modelling this structure can be difficult when the nesting is recursive, e.g.
1425
+ IP tunneling. Here is an example of the simplest possible recursive TLV structure,
1426
+ a [list that can contains atoms or other
1427
+ lists](http://bindata.rubyforge.org/svn/trunk/examples/list.rb).
1328
1428
 
1329
1429
  ---------------------------------------------------------------------------
1330
1430
 
data/spec/array_spec.rb CHANGED
@@ -296,3 +296,23 @@ describe BinData::Array, "nested within an Array" do
296
296
  subject.should == [ [4], [5, 6], [7, 8, 9] ]
297
297
  end
298
298
  end
299
+
300
+ describe BinData::Array, "subclassed" do
301
+ class IntArray < BinData::Array
302
+ endian :big
303
+ default_parameter :initial_element_value => 0
304
+
305
+ uint16 :initial_value => :initial_element_value
306
+ end
307
+
308
+ it "should forward parameters" do
309
+ subject = IntArray.new(:initial_length => 7)
310
+ subject.length.should == 7
311
+ end
312
+
313
+ it "should be able to override default parameters" do
314
+ subject = IntArray.new(:initial_length => 3, :initial_element_value => 5)
315
+ subject.to_binary_s.should == "\x00\x05\x00\x05\x00\x05"
316
+ end
317
+ end
318
+
@@ -5,6 +5,16 @@ require File.expand_path(File.join(File.dirname(__FILE__), "example"))
5
5
  require 'bindata/base_primitive'
6
6
  require 'bindata/io'
7
7
 
8
+ describe BinData::BasePrimitive do
9
+ let(:r) { BinData::RegisteredClasses }
10
+
11
+ it "should not be registered" do
12
+ lambda {
13
+ r.lookup("BasePrimitive")
14
+ }.should raise_error(BinData::UnRegisteredTypeError)
15
+ end
16
+ end
17
+
8
18
  describe BinData::BasePrimitive, "all subclasses" do
9
19
  class SubClassOfBasePrimitive < BinData::BasePrimitive
10
20
  expose_methods_for_testing