bit-struct 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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