bit-struct 0.13.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/.gitignore +2 -0
  2. data/History.txt +102 -0
  3. data/README.txt +189 -0
  4. data/Rakefile +34 -0
  5. data/TODO +23 -0
  6. data/TODO-ALSO +71 -0
  7. data/examples/ara-player-data.rb +82 -0
  8. data/examples/bignum.rb +18 -0
  9. data/examples/bits.rb +19 -0
  10. data/examples/byte-bdy.rb +30 -0
  11. data/examples/field-ripper.rb +22 -0
  12. data/examples/fixed-point.rb +17 -0
  13. data/examples/ip.rb +81 -0
  14. data/examples/longlong.rb +30 -0
  15. data/examples/md.rb +23 -0
  16. data/examples/modular-def.rb +38 -0
  17. data/examples/native.rb +31 -0
  18. data/examples/nested.rb +33 -0
  19. data/examples/pad.rb +14 -0
  20. data/examples/ping-recv.rb +25 -0
  21. data/examples/ping.rb +73 -0
  22. data/examples/player-data.rb +75 -0
  23. data/examples/raw.rb +62 -0
  24. data/examples/rest.rb +30 -0
  25. data/examples/switch-endian.rb +49 -0
  26. data/examples/vector.rb +98 -0
  27. data/lib/bit-struct.rb +6 -0
  28. data/lib/bit-struct/bit-struct.rb +549 -0
  29. data/lib/bit-struct/char-field.rb +48 -0
  30. data/lib/bit-struct/fields.rb +273 -0
  31. data/lib/bit-struct/float-field.rb +61 -0
  32. data/lib/bit-struct/hex-octet-field.rb +20 -0
  33. data/lib/bit-struct/nested-field.rb +76 -0
  34. data/lib/bit-struct/octet-field.rb +45 -0
  35. data/lib/bit-struct/pad-field.rb +15 -0
  36. data/lib/bit-struct/signed-field.rb +258 -0
  37. data/lib/bit-struct/text-field.rb +44 -0
  38. data/lib/bit-struct/unsigned-field.rb +248 -0
  39. data/lib/bit-struct/vector-field.rb +77 -0
  40. data/lib/bit-struct/vector.rb +173 -0
  41. data/lib/bit-struct/yaml.rb +69 -0
  42. data/tasks/ann.rake +80 -0
  43. data/tasks/bones.rake +20 -0
  44. data/tasks/gem.rake +201 -0
  45. data/tasks/git.rake +40 -0
  46. data/tasks/notes.rake +27 -0
  47. data/tasks/post_load.rake +34 -0
  48. data/tasks/rdoc.rake +51 -0
  49. data/tasks/rubyforge.rake +55 -0
  50. data/tasks/setup.rb +292 -0
  51. data/tasks/spec.rake +54 -0
  52. data/tasks/svn.rake +47 -0
  53. data/tasks/test.rake +40 -0
  54. data/tasks/zentest.rake +36 -0
  55. data/test/test-endian.rb +39 -0
  56. data/test/test-vector.rb +38 -0
  57. data/test/test.rb +433 -0
  58. metadata +126 -0
@@ -0,0 +1,54 @@
1
+
2
+ if HAVE_SPEC_RAKE_SPECTASK and not PROJ.spec.files.to_a.empty?
3
+ require 'spec/rake/verify_rcov'
4
+
5
+ namespace :spec do
6
+
7
+ desc 'Run all specs with basic output'
8
+ Spec::Rake::SpecTask.new(:run) do |t|
9
+ t.ruby_opts = PROJ.ruby_opts
10
+ t.spec_opts = PROJ.spec.opts
11
+ t.spec_files = PROJ.spec.files
12
+ t.libs += PROJ.libs
13
+ end
14
+
15
+ desc 'Run all specs with text output'
16
+ Spec::Rake::SpecTask.new(:specdoc) do |t|
17
+ t.ruby_opts = PROJ.ruby_opts
18
+ t.spec_opts = PROJ.spec.opts + ['--format', 'specdoc']
19
+ t.spec_files = PROJ.spec.files
20
+ t.libs += PROJ.libs
21
+ end
22
+
23
+ if HAVE_RCOV
24
+ desc 'Run all specs with RCov'
25
+ Spec::Rake::SpecTask.new(:rcov) do |t|
26
+ t.ruby_opts = PROJ.ruby_opts
27
+ t.spec_opts = PROJ.spec.opts
28
+ t.spec_files = PROJ.spec.files
29
+ t.libs += PROJ.libs
30
+ t.rcov = true
31
+ t.rcov_dir = PROJ.rcov.dir
32
+ t.rcov_opts = PROJ.rcov.opts + ['--exclude', 'spec']
33
+ end
34
+
35
+ RCov::VerifyTask.new(:verify) do |t|
36
+ t.threshold = PROJ.rcov.threshold
37
+ t.index_html = File.join(PROJ.rcov.dir, 'index.html')
38
+ t.require_exact_threshold = PROJ.rcov.threshold_exact
39
+ end
40
+
41
+ task :verify => :rcov
42
+ remove_desc_for_task %w(spec:clobber_rcov)
43
+ end
44
+
45
+ end # namespace :spec
46
+
47
+ desc 'Alias to spec:run'
48
+ task :spec => 'spec:run'
49
+
50
+ task :clobber => 'spec:clobber_rcov' if HAVE_RCOV
51
+
52
+ end # if HAVE_SPEC_RAKE_SPECTASK
53
+
54
+ # EOF
@@ -0,0 +1,47 @@
1
+
2
+ if HAVE_SVN
3
+
4
+ unless PROJ.svn.root
5
+ info = %x/svn info ./
6
+ m = %r/^Repository Root:\s+(.*)$/.match(info)
7
+ PROJ.svn.root = (m.nil? ? '' : m[1])
8
+ end
9
+ PROJ.svn.root = File.join(PROJ.svn.root, PROJ.svn.path) unless PROJ.svn.path.empty?
10
+
11
+ namespace :svn do
12
+
13
+ # A prerequisites task that all other tasks depend upon
14
+ task :prereqs
15
+
16
+ desc 'Show tags from the SVN repository'
17
+ task :show_tags => 'svn:prereqs' do |t|
18
+ tags = %x/svn list #{File.join(PROJ.svn.root, PROJ.svn.tags)}/
19
+ tags.gsub!(%r/\/$/, '')
20
+ tags = tags.split("\n").sort {|a,b| b <=> a}
21
+ puts tags
22
+ end
23
+
24
+ desc 'Create a new tag in the SVN repository'
25
+ task :create_tag => 'svn:prereqs' do |t|
26
+ v = ENV['VERSION'] or abort 'Must supply VERSION=x.y.z'
27
+ abort "Versions don't match #{v} vs #{PROJ.version}" if v != PROJ.version
28
+
29
+ svn = PROJ.svn
30
+ trunk = File.join(svn.root, svn.trunk)
31
+ tag = "%s-%s" % [PROJ.name, PROJ.version]
32
+ tag = File.join(svn.root, svn.tags, tag)
33
+ msg = "Creating tag for #{PROJ.name} version #{PROJ.version}"
34
+
35
+ puts "Creating SVN tag '#{tag}'"
36
+ unless system "svn cp -m '#{msg}' #{trunk} #{tag}"
37
+ abort "Tag creation failed"
38
+ end
39
+ end
40
+
41
+ end # namespace :svn
42
+
43
+ task 'gem:release' => 'svn:create_tag'
44
+
45
+ end # if PROJ.svn.path
46
+
47
+ # EOF
@@ -0,0 +1,40 @@
1
+
2
+ if test(?e, PROJ.test.file) or not PROJ.test.files.to_a.empty?
3
+ require 'rake/testtask'
4
+
5
+ namespace :test do
6
+
7
+ Rake::TestTask.new(:run) do |t|
8
+ t.libs = PROJ.libs
9
+ t.test_files = if test(?f, PROJ.test.file) then [PROJ.test.file]
10
+ else PROJ.test.files end
11
+ t.ruby_opts += PROJ.ruby_opts
12
+ t.ruby_opts += PROJ.test.opts
13
+ end
14
+
15
+ if HAVE_RCOV
16
+ desc 'Run rcov on the unit tests'
17
+ task :rcov => :clobber_rcov do
18
+ opts = PROJ.rcov.opts.dup << '-o' << PROJ.rcov.dir
19
+ opts = opts.join(' ')
20
+ files = if test(?f, PROJ.test.file) then [PROJ.test.file]
21
+ else PROJ.test.files end
22
+ files = files.join(' ')
23
+ sh "#{RCOV} #{files} #{opts}"
24
+ end
25
+
26
+ task :clobber_rcov do
27
+ rm_r 'coverage' rescue nil
28
+ end
29
+ end
30
+
31
+ end # namespace :test
32
+
33
+ desc 'Alias to test:run'
34
+ task :test => 'test:run'
35
+
36
+ task :clobber => 'test:clobber_rcov' if HAVE_RCOV
37
+
38
+ end
39
+
40
+ # EOF
@@ -0,0 +1,36 @@
1
+ if HAVE_ZENTEST
2
+
3
+ # --------------------------------------------------------------------------
4
+ if test(?e, PROJ.test.file) or not PROJ.test.files.to_a.empty?
5
+ require 'autotest'
6
+
7
+ namespace :test do
8
+ task :autotest do
9
+ Autotest.run
10
+ end
11
+ end
12
+
13
+ desc "Run the autotest loop"
14
+ task :autotest => 'test:autotest'
15
+
16
+ end # if test
17
+
18
+ # --------------------------------------------------------------------------
19
+ if HAVE_SPEC_RAKE_SPECTASK and not PROJ.spec.files.to_a.empty?
20
+ require 'autotest/rspec'
21
+
22
+ namespace :spec do
23
+ task :autotest do
24
+ load '.autotest' if test(?f, '.autotest')
25
+ Autotest::Rspec.run
26
+ end
27
+ end
28
+
29
+ desc "Run the autotest loop"
30
+ task :autotest => 'spec:autotest'
31
+
32
+ end # if rspec
33
+
34
+ end # if HAVE_ZENTEST
35
+
36
+ # EOF
@@ -0,0 +1,39 @@
1
+ require 'test/unit'
2
+ require 'bit-struct'
3
+
4
+ class Test_Endian < Test::Unit::TestCase
5
+ class Endian < BitStruct
6
+ unsigned :f_big, 32, :endian => :big
7
+ unsigned :f_little, 32, :endian => :little
8
+ unsigned :f_native, 32, :endian => :native
9
+ unsigned :f_network, 32, :endian => :network
10
+ end
11
+
12
+ attr_reader :bs
13
+
14
+ def setup
15
+ @bs = Endian.new
16
+ bs.f_big = bs.f_little = bs.f_native = bs.f_network = 0x01020304
17
+ end
18
+
19
+ def test_readers
20
+ assert_equal(0x01020304, bs.f_big)
21
+ assert_equal(0x01020304, bs.f_little)
22
+ assert_equal(0x01020304, bs.f_native)
23
+ assert_equal(0x01020304, bs.f_network)
24
+ end
25
+
26
+ def test_writers
27
+ bs.fields.each do |field|
28
+ byte_offset = field.offset / 8
29
+ valstr = bs.to_s[byte_offset, 4]
30
+ case field.options[:endian]
31
+ when :big, :network
32
+ assert_equal("\01\02\03\04", valstr)
33
+ when :little
34
+ assert_equal("\04\03\02\01", valstr)
35
+ when :native
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,38 @@
1
+ require 'test/unit'
2
+ require 'bit-struct'
3
+
4
+ class Test_Vector < Test::Unit::TestCase
5
+ class Packet < BitStruct
6
+ unsigned :stuff, 32, "whatever"
7
+
8
+ vector :v, "a vector", :length => 5 do
9
+ unsigned :x, 16
10
+ signed :y, 32
11
+ end
12
+
13
+ unsigned :other, 16, "other stuff"
14
+ end
15
+
16
+ attr_reader :pkt
17
+
18
+ def setup
19
+ @pkt = Packet.new
20
+ end
21
+
22
+ def test_length
23
+ assert_equal(Packet.round_byte_length, pkt.length)
24
+ end
25
+
26
+ def test_writers
27
+ assert_equal(pkt.v[2].x, 0)
28
+ v = pkt.v
29
+ xy = v[2]
30
+ xy.x = 3
31
+ xy.y = -4
32
+ v[2] = xy
33
+ assert_equal(pkt.v[2].x, 0)
34
+ pkt.v = v
35
+ assert_equal(pkt.v[2].x, 3)
36
+ assert_equal(pkt.v[2].y, -4)
37
+ end
38
+ end
@@ -0,0 +1,433 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require 'bit-struct'
5
+
6
+ class Test_BitStruct < Test::Unit::TestCase
7
+
8
+ class T1 < BitStruct
9
+ unsigned :foo, 8
10
+ end
11
+
12
+ class T2 < BitStruct
13
+ unsigned :bar, 8
14
+ end
15
+
16
+ class Rest < BitStruct
17
+ unsigned :zap, 8
18
+ rest :body, T2
19
+ end
20
+
21
+ class NestedPart < BitStruct
22
+ unsigned :x, 5
23
+ unsigned :y, 3, :default => 2
24
+ char :s, 5*8
25
+ end
26
+
27
+ class Container < BitStruct
28
+ nest :n1, NestedPart, :default => NestedPart.new(:x=>1, :s=>"deflt")
29
+ nest :n2, NestedPart
30
+ end
31
+
32
+ class Overflow < BitStruct
33
+ unsigned :f1, 3
34
+ unsigned :f2, 5
35
+ unsigned :f3, 8
36
+ unsigned :f4, 16
37
+ unsigned :f5, 32
38
+ char :f6, 24
39
+ unsigned :f7, 8
40
+ end
41
+
42
+ class BS < BitStruct
43
+ def self.next_name
44
+ (@@next_name ||= "f000").succ!.dup
45
+ end
46
+
47
+ unsigned next_name, 13
48
+ unsigned next_name, 6
49
+ signed next_name, 5
50
+
51
+ char next_name, 16
52
+
53
+ unsigned next_name, 2
54
+ signed next_name, 3, :default => -2
55
+ unsigned next_name, 3
56
+
57
+ unsigned f1=next_name, 16
58
+
59
+ signed next_name, 8
60
+ signed next_name, 16, :endian => :little
61
+ signed next_name, 16, :endian => :native
62
+ signed f2=next_name, 32
63
+ unsigned next_name, 56, :endian => :big
64
+ signed next_name, 48, :endian => :little
65
+
66
+ char next_name, 8, :default => "b"
67
+ float next_name, 32, :default => 1.23
68
+ float next_name, 64, :format => "%10.5f"
69
+
70
+ octets next_name, 32, :default => "192.168.1.123"
71
+
72
+ hex_octets next_name, 48, :default => "ab:cd:ef:01:23:45"
73
+
74
+ unsigned next_name, 1
75
+ unsigned next_name, 11, :fixed => 100 # unaligned!
76
+ signed next_name, 7 # unaligned!
77
+ signed next_name, 14 # unaligned and touches 3 bytes
78
+ signed next_name, 7, :fixed => 1000 # unaligned!
79
+
80
+ char next_name, 24
81
+
82
+ rest :bs_body
83
+
84
+ INITIAL_VALUES = {
85
+ f1 => 1234,
86
+ f2 => 5678
87
+ }
88
+
89
+ INITIAL_VALUES.each do |f, v|
90
+ initial_value.send "#{f}=", v
91
+ end
92
+
93
+ end
94
+
95
+ class BS_1 < BS
96
+
97
+ signed next_name, 4, :fixed => 25
98
+ unsigned next_name, 3, :fixed => 0.01
99
+ unsigned next_name, 1
100
+
101
+ unsigned next_name, 32, :fixed => 1000
102
+
103
+ char next_name, 40
104
+ text next_name, 40
105
+
106
+ unsigned next_name, 8, :default => 0xEF
107
+
108
+ unsigned next_name, 8
109
+
110
+ unsigned next_name, 1
111
+
112
+ rest :bs1_body
113
+
114
+ end
115
+
116
+ def setup
117
+ srand(767343)
118
+ @bs = BS.new
119
+ @bs_1 = BS_1.new
120
+ @simple = [T1.new, T2.new]
121
+ @testers = @simple + [@bs, @bs_1]
122
+ end
123
+
124
+ def test_init
125
+ @testers.each do |bs|
126
+ if defined?(bs.class::INITIAL_VALUES)
127
+ initial_values = bs.class::INITIAL_VALUES
128
+ end
129
+ bs.fields.each do |field|
130
+ iv = initial_values && initial_values[field.name]
131
+ iv ||= field.default
132
+
133
+ if iv
134
+ case field
135
+ when BitStruct::FloatField
136
+ assert_in_delta(iv, bs.send(field.name), 100,
137
+ "In #{field.name} of a #{bs.class}")
138
+ else
139
+ assert_equal(iv, bs.send(field.name),
140
+ "In #{field.name} of a #{bs.class}")
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ def test_init_with_value
148
+ randomize(@bs_1)
149
+
150
+ b = BS_1.new(@bs_1)
151
+ assert_equal(@bs_1, b, "Initialize with argument failed.")
152
+
153
+ c = BS_1.new(b)
154
+ assert_equal(@bs_1, c, "Initialize with argument failed.")
155
+
156
+ b1 = BS_1.new("")
157
+ b2 = BS_1.new(nil)
158
+ assert_equal(b1, b2, "Initialize with short argument failed.")
159
+ end
160
+
161
+ def test_init_with_hash
162
+ randomize(@bs_1)
163
+
164
+ h = {}
165
+ @bs_1.fields.each do |f|
166
+ h[f.name] = @bs_1.send(f.name)
167
+ end
168
+
169
+ b = BS_1.new(h)
170
+ assert_equal(@bs_1, b, "Initialize with argument failed.")
171
+ end
172
+
173
+ def test_join
174
+ assert_equal(@bs+@bs_1, BitStruct.join(@bs,@bs_1))
175
+ assert_equal(@bs+@bs_1, [@bs,@bs_1].join(""))
176
+ end
177
+
178
+ def test_parse
179
+ orig = @testers
180
+ orig.each do |bs|
181
+ randomize(bs)
182
+ end
183
+
184
+ data = BitStruct.join(orig)
185
+ round_trip = BitStruct.parse(data, orig.map{|bs|bs.class})
186
+ orig.zip(round_trip) do |bs1, bs2|
187
+ assert_equal(bs1, bs2)
188
+ end
189
+ end
190
+
191
+ def test_closed
192
+ assert_raises(BitStruct::ClosedClassError) do
193
+ BS.class_eval do
194
+ unsigned :foo, 3
195
+ end
196
+ end
197
+ end
198
+
199
+ def test_rest
200
+ len0 = @bs_1.length
201
+
202
+ @bs_1.bs1_body = "a"*50
203
+ assert_equal("a"*50, @bs_1.bs1_body)
204
+ assert_equal(len0+50, @bs_1.length)
205
+
206
+ @bs_1.bs1_body = "b"*60
207
+ assert_equal("b"*60, @bs_1.bs1_body)
208
+ assert_equal(len0+60, @bs_1.length)
209
+
210
+ @bs_1.bs1_body = "c"*40
211
+ assert_equal("c"*40, @bs_1.bs1_body)
212
+ assert_equal(len0+40, @bs_1.length)
213
+
214
+ @bs_1.bs1_body = ""
215
+ assert_equal("", @bs_1.bs1_body)
216
+ assert_equal(len0, @bs_1.length)
217
+ end
218
+
219
+ def test_rest_with_class
220
+ r = Rest.new
221
+ t2 = T2.new
222
+ t2.bar = 123
223
+ r.body = t2
224
+ assert_equal(123, r.body.bar)
225
+ end
226
+
227
+ def test_nest
228
+ cont = Container.new
229
+ n1 = cont.n1
230
+
231
+ assert_equal(1, n1.x)
232
+ assert_equal(2, n1.y)
233
+ assert_equal("deflt", n1.s)
234
+
235
+ n1.sub(/./, " ")
236
+
237
+ assert_equal(1, n1.x)
238
+ assert_equal(2, n1.y)
239
+ assert_equal("deflt", n1.s)
240
+
241
+ n1 = cont.n1
242
+ n2 = cont.n2
243
+
244
+ assert_equal(0, n2.x)
245
+ assert_equal(2, n2.y)
246
+ assert_equal("\0"*5, n2.s)
247
+
248
+ n = NestedPart.new(:x=>4, :y=>1, :s=>"qwert")
249
+ cont.n1 = n
250
+
251
+ assert_equal(4, cont.n1.x)
252
+ assert_equal(1, cont.n1.y)
253
+ assert_equal("qwert", cont.n1.s)
254
+
255
+ assert_raises(ArgumentError) {cont.n2 = Container.new}
256
+ end
257
+
258
+ def test_overflow
259
+ ov = Overflow.new
260
+ empty = ov.dup
261
+
262
+ ov.fields.each do |field|
263
+ ov.gsub(/./, "\0")
264
+
265
+ case field
266
+ when BitStruct::CharField
267
+ val1 = "a" * (field.length+5)
268
+ val2 = ""
269
+
270
+ when BitStruct::UnsignedField
271
+ val1 = 2**32 - 5**10 # mixed bit pattern
272
+ val2 = 0
273
+ end
274
+
275
+ ov.send("#{field.name}=", val1)
276
+ ov.send("#{field.name}=", val2)
277
+
278
+ assert_equal(empty, ov)
279
+ end
280
+ end
281
+
282
+ def test_access
283
+ repeat_access_test(@bs, 10)
284
+ end
285
+
286
+ def test_inheritance
287
+ assert_equal(@bs.fields.size, @bs_1.fields.size - BS_1.own_fields.size,
288
+ "Wrong number of fields inherited in BS_1.")
289
+
290
+ repeat_access_test(@bs_1, 10)
291
+ end
292
+
293
+ def test_initial_value
294
+ bs = @bs
295
+ bs.class::INITIAL_VALUES.each do |f,v|
296
+ assert_equal(v, bs.send(f), "In #{f} of a #{bs.class}")
297
+ end
298
+ end
299
+
300
+ def test_inherited_initial_value
301
+ bs = @bs_1
302
+ bs.class::INITIAL_VALUES.each do |f,v|
303
+ assert_equal(v, bs.send(f), "In #{f} of a #{bs.class}")
304
+ end
305
+ end
306
+
307
+ def test_to_h
308
+ h = @bs_1.to_h(:convert_keys => :to_s)
309
+ field_names = @bs_1.fields.map{|f|f.name.to_s}
310
+ assert_equal(field_names.sort, h.keys.sort)
311
+ field_names.each do |name|
312
+ assert_equal(@bs_1.send(name), h[name])
313
+ end
314
+ end
315
+
316
+ def test_format_option
317
+ formatted_fields = @bs.fields.select {|f|f.format}
318
+ formatted_fields.each do |f|
319
+ val = @bs.send(f.name)
320
+ assert_equal(f.format % val, f.inspect_in_object(@bs, {}))
321
+ end
322
+ end
323
+
324
+ def test_yaml
325
+ assert_equal(@bs_1, YAML.load(@bs_1.to_yaml))
326
+ end
327
+
328
+ def test_field_by_name
329
+ name = :f007
330
+ f = @bs.field_by_name(name)
331
+ assert(f)
332
+ assert_equal(f.name, name)
333
+ end
334
+
335
+ #--------
336
+ def repeat_access_test(bs, n)
337
+ last_set_value = {}
338
+
339
+ start_length = bs.length
340
+
341
+ n.times do
342
+ bs.fields.each do |field|
343
+ last_set_value[field] = randomize_field(bs, field)
344
+
345
+ bs.fields.each do |f2|
346
+ lsv2 = last_set_value[f2]
347
+ if lsv2
348
+ case f2
349
+ when BitStruct::FloatField
350
+ assert_in_delta(lsv2, bs.send(f2.name), 100)
351
+ else
352
+ begin
353
+ assert_equal(lsv2, bs.send(f2.name))
354
+ rescue Test::Unit::AssertionFailedError => ex
355
+ msg =
356
+ "In #{f2.inspect} after setting #{field.inspect} to" +
357
+ " #{last_set_value[field].inspect}"
358
+ raise ex, msg + "\n" + ex.message, ex.backtrace
359
+ end
360
+ end
361
+ end
362
+ end
363
+ end
364
+ end
365
+
366
+ finish_length = bs.length
367
+
368
+ assert_equal(start_length, finish_length, "Length differs after test!")
369
+ end
370
+
371
+ def randomize(bs)
372
+ bs.fields.each do |f|
373
+ randomize_field(bs, f)
374
+ end
375
+ end
376
+
377
+ def randomize_field(bs, field)
378
+ case field
379
+ when BitStruct::SignedField
380
+ divisor = field.options[:fixed]
381
+ if divisor
382
+ value = (rand(2**field.size) - 2**(field.size-1))/divisor.to_f
383
+ else
384
+ value = rand(2**field.size) - 2**(field.size-1)
385
+ end
386
+ bs.send "#{field.name}=", value
387
+ last_set_value = value
388
+
389
+ when BitStruct::UnsignedField
390
+ divisor = field.options[:fixed]
391
+ if divisor
392
+ value = rand(2**field.size)/divisor.to_f
393
+ else
394
+ value = rand(2**field.size)
395
+ ## should ensure that there are some very low and very high walues
396
+ ## esp. 0, 1, 2**n - 1, and so on
397
+ end
398
+ bs.send "#{field.name}=", value
399
+ last_set_value = value
400
+
401
+ when BitStruct::HexOctetField
402
+ val = (1..field.length/8).map {"%02x" % rand(256)}.join(":")
403
+ bs.send "#{field.name}=", val
404
+ last_set_value = val
405
+
406
+ when BitStruct::OctetField
407
+ val = (1..field.length/8).map {"%d" % rand(256)}.join(".")
408
+ bs.send "#{field.name}=", val
409
+ last_set_value = val
410
+
411
+ when BitStruct::CharField
412
+ s = (rand(64)+32).chr
413
+ value = s * (field.length/8)
414
+ bs.send "#{field.name}=", value
415
+ last_set_value = value
416
+
417
+ when BitStruct::TextField
418
+ s = (rand(64)+32).chr
419
+ value = s * rand(field.length*2/8)
420
+ bs.send "#{field.name}=", value
421
+ last_set_value = s * [field.length/8, value.length].min
422
+
423
+ when BitStruct::FloatField
424
+ value = rand(2**30)
425
+ bs.send "#{field.name}=", value
426
+ last_set_value = value
427
+
428
+ else raise
429
+ end
430
+
431
+ return last_set_value
432
+ end
433
+ end