bitstream 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.en +4 -0
- data/lib/bitstream.rb +198 -69
- data/lib/lazy-string.rb +117 -0
- data/sample/{front.rb → flac-front.rb} +0 -0
- data/test/test-array.rb +43 -1
- data/test/test-dynarray.rb +0 -0
- data/test/test-lazy-string.rb +36 -0
- data/test/test-overload.rb +69 -0
- data/test/test-primitives.rb +6 -6
- data/test/test-suite.rb +2 -1
- metadata +64 -46
- data/lib/types/basetype.rb +0 -9
- data/lib/types/utils.rb +0 -26
- data/test/types/test-utils.rb +0 -36
data/README.en
CHANGED
@@ -65,6 +65,10 @@ gzip-viewer.rb: A viewer of the original file name of a gzip file.
|
|
65
65
|
puts "The gzip does not contain its original file name."
|
66
66
|
end
|
67
67
|
|
68
|
+
== Documentation
|
69
|
+
|
70
|
+
In preparation.
|
71
|
+
|
68
72
|
== License
|
69
73
|
|
70
74
|
This library is distributed under the dual license of the Ruby license (in the narrow sense) and the 2-clause BSD license. Please see http://www.ruby-lang.org and BSDL.
|
data/lib/bitstream.rb
CHANGED
@@ -1,22 +1,31 @@
|
|
1
1
|
# Author:: Natsuki Kawai (natsuki.kawai@gmail.com)
|
2
|
-
# Copyright:: Copyright 2011 Natsuki Kawai
|
2
|
+
# Copyright:: Copyright (c) 2011, 2012 Natsuki Kawai
|
3
3
|
# License:: 2-clause BSDL or Ruby's
|
4
4
|
|
5
5
|
require 'random-accessible'
|
6
|
+
require 'lazy-string'
|
6
7
|
|
7
8
|
require 'types/integer'
|
8
9
|
require 'types/string'
|
9
10
|
require 'types/cstring'
|
10
11
|
require 'types/character'
|
11
12
|
|
13
|
+
require 'bitstream/utils'
|
14
|
+
|
12
15
|
module BitStream
|
13
16
|
|
14
17
|
class BitStreamError < Exception
|
15
18
|
end
|
16
19
|
|
17
20
|
Properties = Struct.new(
|
18
|
-
:curr_offset,
|
19
|
-
:
|
21
|
+
:curr_offset,
|
22
|
+
:fields,
|
23
|
+
:mode,
|
24
|
+
:raw_data,
|
25
|
+
:initial_offset,
|
26
|
+
:user_props,
|
27
|
+
:eval_queue,
|
28
|
+
:substreams
|
20
29
|
)
|
21
30
|
|
22
31
|
class ArrayProxy
|
@@ -43,9 +52,6 @@ module BitStream
|
|
43
52
|
def read_access(pos)
|
44
53
|
unless @updated[pos]
|
45
54
|
field = @fields[pos]
|
46
|
-
unless field.has_read?
|
47
|
-
BitStream.read_one_field(field, @instance)
|
48
|
-
end
|
49
55
|
@values[pos] = field.value
|
50
56
|
@fields[pos] = nil
|
51
57
|
@updated[pos] = true
|
@@ -78,6 +84,8 @@ module BitStream
|
|
78
84
|
|
79
85
|
alias :enq :push
|
80
86
|
alias :deq :shift
|
87
|
+
alias :peek_front :first
|
88
|
+
alias :peek_back :last
|
81
89
|
|
82
90
|
end
|
83
91
|
|
@@ -104,59 +112,69 @@ module BitStream
|
|
104
112
|
#end
|
105
113
|
end
|
106
114
|
|
107
|
-
|
115
|
+
class SubStreamPacket
|
108
116
|
|
109
|
-
def self.
|
110
|
-
|
111
|
-
|
117
|
+
def self.instance(length)
|
118
|
+
new length
|
119
|
+
end
|
120
|
+
|
121
|
+
def initialize(length)
|
122
|
+
if length % 8 != 0
|
123
|
+
raise NotImplementedError, "non-aligned substream has not been supported."
|
124
|
+
end
|
125
|
+
@length = length
|
112
126
|
end
|
113
127
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
"
|
128
|
+
attr_reader :length
|
129
|
+
|
130
|
+
def read(s, offset)
|
131
|
+
if offset % 8 != 0
|
132
|
+
raise NotImplementedError, "non-aligned substream has not been supported."
|
119
133
|
end
|
134
|
+
return [LazyString.new(s, offset / 8, @length / 8), @length]
|
120
135
|
end
|
121
136
|
|
122
137
|
end
|
123
138
|
|
124
|
-
|
125
|
-
props = instance.bitstream_properties
|
126
|
-
queue = props.eval_queue
|
139
|
+
class ReaderArray
|
127
140
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
length = field.length
|
132
|
-
length = field.decide_length if length.nil?
|
133
|
-
props.curr_offset += length
|
141
|
+
def initialize
|
142
|
+
@array = []
|
143
|
+
@read = []
|
134
144
|
end
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
props.curr_offset += length
|
145
|
+
|
146
|
+
def [](pos)
|
147
|
+
unless @read[pos]
|
148
|
+
@read[pos] = true
|
149
|
+
reader = @array[pos]
|
150
|
+
if reader.value.nil?
|
151
|
+
reader.read
|
152
|
+
end
|
153
|
+
@array[pos] = reader.value
|
154
|
+
end
|
155
|
+
return @array[pos]
|
147
156
|
end
|
148
|
-
|
157
|
+
|
158
|
+
def <<(reader)
|
159
|
+
@array << reader
|
160
|
+
end
|
161
|
+
|
149
162
|
end
|
150
|
-
|
151
|
-
class
|
163
|
+
|
164
|
+
class FieldReader
|
152
165
|
|
153
|
-
def initialize(type,
|
166
|
+
def initialize(type, instance)
|
154
167
|
@type = type
|
155
|
-
@
|
168
|
+
@instance = instance
|
156
169
|
@length = @type.length if @type.respond_to? :length
|
157
170
|
@has_read = false
|
158
171
|
end
|
159
172
|
|
173
|
+
def props
|
174
|
+
@instance.bitstream_properties
|
175
|
+
end
|
176
|
+
private :props
|
177
|
+
|
160
178
|
def has_read?
|
161
179
|
@has_read
|
162
180
|
end
|
@@ -164,29 +182,40 @@ module BitStream
|
|
164
182
|
def read
|
165
183
|
unless @has_read
|
166
184
|
if @offset.nil?
|
167
|
-
|
168
|
-
else
|
169
|
-
@value, @length = @type.read(@raw_data, @offset)
|
170
|
-
@has_read = true
|
185
|
+
index
|
171
186
|
end
|
187
|
+
@value, @length = @type.read(props.raw_data, @offset)
|
188
|
+
@has_read = true
|
172
189
|
end
|
173
190
|
return @value
|
174
191
|
end
|
192
|
+
|
193
|
+
alias :value :read
|
175
194
|
|
176
|
-
def
|
195
|
+
def length
|
177
196
|
# @length must not be nil if @has_read.
|
178
197
|
if @length.nil?
|
179
198
|
if @offset.nil?
|
180
|
-
|
199
|
+
index
|
181
200
|
else
|
182
|
-
@value, @length = @type.read(
|
201
|
+
@value, @length = @type.read(props.raw_data, @offset)
|
183
202
|
@has_read = true
|
184
203
|
end
|
185
204
|
end
|
186
205
|
return @length
|
187
206
|
end
|
188
207
|
|
189
|
-
|
208
|
+
def index
|
209
|
+
queue = props.eval_queue
|
210
|
+
|
211
|
+
while @offset.nil?
|
212
|
+
field = queue.deq
|
213
|
+
field.offset = props.curr_offset
|
214
|
+
length = field.length
|
215
|
+
props.curr_offset += length
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
190
219
|
attr_accessor :offset
|
191
220
|
|
192
221
|
end
|
@@ -198,10 +227,33 @@ module BitStream
|
|
198
227
|
@fields = {}
|
199
228
|
@types = types.dup
|
200
229
|
@index = 0
|
230
|
+
@singleton_props = {}
|
201
231
|
@class_props = {}
|
232
|
+
@class_props_chain = [@class_props]
|
202
233
|
@bitstream_mutex = Mutex.new
|
203
234
|
end
|
204
235
|
|
236
|
+
# Currently BitStream does not support inheritance.
|
237
|
+
if false
|
238
|
+
def on_inherit(types, chain, fields, mutex)
|
239
|
+
@field_defs = []
|
240
|
+
@fields = fields
|
241
|
+
@types = types
|
242
|
+
@index = 0
|
243
|
+
@class_props = {}
|
244
|
+
@class_props_chain = [@class_props]
|
245
|
+
@class_props_chain.concat(chain)
|
246
|
+
@bitstream_mutex = mutex
|
247
|
+
end
|
248
|
+
|
249
|
+
def inherited(subclass)
|
250
|
+
subclass.on_inherit(@types, @class_props_chain, @fields, @bitstream_mutex)
|
251
|
+
def subclass.fields
|
252
|
+
raise NameError, "Cannot define fields on a subclass of a class includes BitStream."
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
205
257
|
def fields(&field_def)
|
206
258
|
@field_defs << field_def
|
207
259
|
end
|
@@ -209,7 +261,14 @@ module BitStream
|
|
209
261
|
def initialize_instance(raw_data, instance)
|
210
262
|
props = instance.bitstream_properties
|
211
263
|
props.mode = :field_def
|
212
|
-
|
264
|
+
|
265
|
+
user_props = props.user_props
|
266
|
+
|
267
|
+
@class_props_chain.each do |class_props|
|
268
|
+
user_props.merge!(class_props)
|
269
|
+
end
|
270
|
+
user_props[:nest_chain] = user_props[:nest_chain] + [instance]
|
271
|
+
|
213
272
|
@bitstream_mutex.synchronize do
|
214
273
|
@instance = instance
|
215
274
|
|
@@ -217,9 +276,16 @@ module BitStream
|
|
217
276
|
field_def.call
|
218
277
|
end
|
219
278
|
end
|
279
|
+
substream_types = @singleton_props[:substream_types]
|
280
|
+
substreams = props.substreams
|
281
|
+
unless substream_types.nil?
|
282
|
+
substreams.keys.each do |id|
|
283
|
+
# TODO: Support multi type substreams.
|
284
|
+
substreams[id] = substream_types[0].instance.read(substreams[id])
|
285
|
+
end
|
286
|
+
end
|
220
287
|
end
|
221
288
|
|
222
|
-
|
223
289
|
def self.types
|
224
290
|
@types
|
225
291
|
end
|
@@ -254,6 +320,10 @@ module BitStream
|
|
254
320
|
@class_props[:byte_order] = order.intern
|
255
321
|
end
|
256
322
|
|
323
|
+
def substream_types(*types)
|
324
|
+
@singleton_props[:substream_types] = types
|
325
|
+
end
|
326
|
+
|
257
327
|
def self.add_type(type, name = nil, bs = self)
|
258
328
|
bs.instance_eval do
|
259
329
|
define_method(name) do |*args|
|
@@ -274,18 +344,13 @@ module BitStream
|
|
274
344
|
else
|
275
345
|
type_instance = type.instance(user_props, *args)
|
276
346
|
end
|
277
|
-
field =
|
347
|
+
field = FieldReader.new(type_instance, @instance)
|
278
348
|
queue.enq(field)
|
279
|
-
@instance.bitstream_properties.fields[name] = field
|
280
349
|
|
281
350
|
name_in_method = name
|
282
351
|
|
283
352
|
@instance.singleton_class.instance_eval do
|
284
353
|
define_method name do
|
285
|
-
field = bitstream_properties.fields[name_in_method]
|
286
|
-
if field.value.nil?
|
287
|
-
BitStream.read_one_field(field, self)
|
288
|
-
end
|
289
354
|
field.value
|
290
355
|
end
|
291
356
|
end
|
@@ -325,10 +390,21 @@ module BitStream
|
|
325
390
|
case props.mode
|
326
391
|
when :field_def
|
327
392
|
field = ArrayProxy.new(@instance)
|
328
|
-
size.
|
329
|
-
|
330
|
-
|
331
|
-
|
393
|
+
if size.respond_to?(:to_int) && size >= 0
|
394
|
+
size.times do
|
395
|
+
field_element = FieldReader.new(type_instance, @instance)
|
396
|
+
field.add_field(field_element)
|
397
|
+
queue.enq(field_element)
|
398
|
+
end
|
399
|
+
else
|
400
|
+
queue.peek_back.index unless queue.empty?
|
401
|
+
while props.curr_offset < props.raw_data.bytesize * 8
|
402
|
+
field_element = FieldReader.new(type_instance, @instance)
|
403
|
+
field.add_field(field_element)
|
404
|
+
queue.enq(field_element)
|
405
|
+
field_element.index
|
406
|
+
#puts "curr_offset:#{props.curr_offset} bytesize:#{props.raw_data.bytesize}"
|
407
|
+
end
|
332
408
|
end
|
333
409
|
|
334
410
|
@instance.singleton_class.instance_eval do
|
@@ -374,7 +450,7 @@ module BitStream
|
|
374
450
|
if fields[name].nil?
|
375
451
|
fields[name] = ArrayProxy.new(@instance)
|
376
452
|
end
|
377
|
-
field =
|
453
|
+
field = FieldReader.new(type_instance, @instance)
|
378
454
|
fields[name].add_field(field)
|
379
455
|
queue.enq(field)
|
380
456
|
|
@@ -394,6 +470,34 @@ module BitStream
|
|
394
470
|
end
|
395
471
|
end
|
396
472
|
end
|
473
|
+
|
474
|
+
def substream(name, id, length)
|
475
|
+
name = name.intern
|
476
|
+
props = @instance.bitstream_properties
|
477
|
+
user_props = props.user_props
|
478
|
+
raw_data = props.raw_data
|
479
|
+
queue = props.eval_queue
|
480
|
+
top_stream = @instance.bitstream_properties.user_props[:nest_chain].first
|
481
|
+
substreams = top_stream.bitstream_properties.substreams
|
482
|
+
|
483
|
+
case props.mode
|
484
|
+
when :field_def
|
485
|
+
type_instance = SubStreamPacket.instance(length)
|
486
|
+
field = FieldReader.new(type_instance, @instance)
|
487
|
+
queue.enq(field)
|
488
|
+
field.read
|
489
|
+
|
490
|
+
substreams[id] << LazyString.new if substreams[id].empty?
|
491
|
+
substreams[id].last << field.value
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
def separate_substream(id)
|
496
|
+
# TODO: Refactor here.
|
497
|
+
top_stream = @instance.bitstream_properties.user_props[:nest_chain].first
|
498
|
+
substreams = top_stream.bitstream_properties.substreams
|
499
|
+
substreams[id] << LazyString.new
|
500
|
+
end
|
397
501
|
|
398
502
|
def add_type(type, name = nil)
|
399
503
|
if name.nil?
|
@@ -412,17 +516,29 @@ module BitStream
|
|
412
516
|
end
|
413
517
|
|
414
518
|
def create_with_offset(s, offset, props = {})
|
519
|
+
props[:nest_chain] = [] unless props.include?(:nest_chain)
|
415
520
|
klass = Class.new(self)
|
416
|
-
instance = klass.new
|
521
|
+
instance = klass.new
|
522
|
+
instance.initialize_properties(s, offset)
|
417
523
|
instance.bitstream_properties.user_props = props
|
418
524
|
initialize_instance(s, instance)
|
525
|
+
instance.initialize_with_fields
|
419
526
|
return instance
|
420
527
|
end
|
421
528
|
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
529
|
+
def method_missing(name, *args)
|
530
|
+
name_s = name.to_s
|
531
|
+
field_name = args.shift
|
532
|
+
if name_s =~ /^uint(\d+)$/
|
533
|
+
bit_width = Regexp.last_match[1].to_i
|
534
|
+
unsigned field_name, bit_width, *args
|
535
|
+
elsif name_s =~ /^int(\d+)$/
|
536
|
+
bit_width = Regexp.last_match[1].to_i
|
537
|
+
signed field_name, bit_width, *args
|
538
|
+
else
|
539
|
+
super name, args
|
540
|
+
end
|
541
|
+
end
|
426
542
|
|
427
543
|
def instance(inherited_props, user_props = {})
|
428
544
|
NestWrapper.new(self, inherited_props, user_props)
|
@@ -435,22 +551,35 @@ module BitStream
|
|
435
551
|
obj.initialize_for_class_methods(ClassMethods.types)
|
436
552
|
end
|
437
553
|
|
438
|
-
def
|
554
|
+
def initialize_with_fields
|
555
|
+
# Nothing to do.
|
556
|
+
# Override me if you want to do anything after all fields has been defined.
|
557
|
+
end
|
558
|
+
|
559
|
+
def initialize_properties(s, offset = 0)
|
439
560
|
props = Properties.new
|
440
561
|
props.curr_offset = offset
|
441
562
|
props.fields = {}
|
442
563
|
props.raw_data = s
|
443
564
|
props.initial_offset = offset
|
444
565
|
props.eval_queue = Queue.new
|
566
|
+
props.substreams = Hash.new do |hash, key|
|
567
|
+
hash[key] = []
|
568
|
+
end
|
445
569
|
@bitstream_properties = props
|
446
570
|
end
|
447
571
|
|
448
572
|
def length
|
449
|
-
BitStream.index_all_fields(self)
|
450
573
|
props = @bitstream_properties
|
574
|
+
queue = props.eval_queue
|
575
|
+
queue.peek_back.index unless queue.empty?
|
451
576
|
props.curr_offset - props.initial_offset
|
452
577
|
end
|
453
578
|
|
579
|
+
def substreams
|
580
|
+
@bitstream_properties.substreams.values
|
581
|
+
end
|
582
|
+
|
454
583
|
#def properties=(props)
|
455
584
|
# Method to override.
|
456
585
|
#end
|
data/lib/lazy-string.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# Author:: Natsuki Kawai (natsuki.kawai@gmail.com)
|
2
|
+
# Copyright:: Copyright (c) 2012 Natsuki Kawai
|
3
|
+
# License:: 2-clause BSDL or Ruby's
|
4
|
+
|
5
|
+
class LazyString
|
6
|
+
|
7
|
+
# TODO: change name of SubString.
|
8
|
+
SubString = Struct.new(:start, :value)
|
9
|
+
|
10
|
+
class LazySubString
|
11
|
+
|
12
|
+
def initialize(value, start, size)
|
13
|
+
@start = start
|
14
|
+
@size = size
|
15
|
+
@value = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](*args)
|
19
|
+
if args.size == 2
|
20
|
+
start = args[0]
|
21
|
+
length = args[1]
|
22
|
+
return @value[@start + start, length]
|
23
|
+
else
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_str
|
29
|
+
@value[@start, @size].to_str
|
30
|
+
end
|
31
|
+
alias :to_s :to_str
|
32
|
+
|
33
|
+
attr_reader :size
|
34
|
+
alias :length :size
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(*args)
|
39
|
+
@size = 0
|
40
|
+
@chain = []
|
41
|
+
|
42
|
+
if args.size == 3
|
43
|
+
@chain << SubString.new(0, LazySubString.new(*args))
|
44
|
+
@size = args[2]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# TODO: Add 'add' method to add a substring of a String.
|
49
|
+
def <<(other)
|
50
|
+
if other.respond_to?(:to_int)
|
51
|
+
return self << ('' << other)
|
52
|
+
end
|
53
|
+
|
54
|
+
@chain << SubString.new(@size, other)
|
55
|
+
@size += other.size
|
56
|
+
end
|
57
|
+
|
58
|
+
def [](*args)
|
59
|
+
if args.size == 2
|
60
|
+
start = args[0]
|
61
|
+
last = start + args[1]
|
62
|
+
length = args[1]
|
63
|
+
e = @chain.each
|
64
|
+
|
65
|
+
curr = nil
|
66
|
+
begin
|
67
|
+
curr = e.next
|
68
|
+
end while curr.start + curr.value.size < start
|
69
|
+
|
70
|
+
value = curr.value
|
71
|
+
sub_start = start - curr.start
|
72
|
+
sub_length = value.size - sub_start
|
73
|
+
if sub_length >= length
|
74
|
+
return LazyString.new(value, sub_start, length)
|
75
|
+
else
|
76
|
+
res = LazyString.new(value, sub_start, sub_length)
|
77
|
+
length -= sub_length
|
78
|
+
while length > 0
|
79
|
+
curr = e.next
|
80
|
+
if length < curr.value.length
|
81
|
+
res << LazyString.new(curr.value, 0, length)
|
82
|
+
length = 0
|
83
|
+
else
|
84
|
+
res << curr.value
|
85
|
+
length -= curr.value.length
|
86
|
+
end
|
87
|
+
end
|
88
|
+
return res
|
89
|
+
end
|
90
|
+
elsif args.respond_to?(:to_int)
|
91
|
+
warn "#{self.class.name}#[pos] has not implemented yet. Calling #{self.class.name}#to_str."
|
92
|
+
to_str[*args]
|
93
|
+
else # args is one Range object.
|
94
|
+
warn "#{self.class.name}#[range] has not implemented yet. Calling #{self.class.name}#to_str."
|
95
|
+
to_str[*args]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def unpack(template)
|
100
|
+
# TODO: Optimize here.
|
101
|
+
# (Stop concat the front strings if template start with "x".)
|
102
|
+
to_str.unpack(template)
|
103
|
+
end
|
104
|
+
|
105
|
+
def to_str
|
106
|
+
res = ''
|
107
|
+
@chain.each do |substr|
|
108
|
+
res << substr.value
|
109
|
+
end
|
110
|
+
return res
|
111
|
+
end
|
112
|
+
alias :to_s :to_str
|
113
|
+
|
114
|
+
attr_reader :size
|
115
|
+
alias :length :size
|
116
|
+
|
117
|
+
end
|
File without changes
|
data/test/test-array.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Author:: Natsuki Kawai (natsuki.kawai@gmail.com)
|
2
|
-
# Copyright:: Copyright 2011 Natsuki Kawai
|
2
|
+
# Copyright:: Copyright (c) 2011, 2012 Natsuki Kawai
|
3
3
|
# License:: 2-clause BSDL or Ruby's
|
4
4
|
|
5
5
|
|
@@ -16,18 +16,60 @@ class ArraySample
|
|
16
16
|
|
17
17
|
end
|
18
18
|
|
19
|
+
class InfiniteArraySample
|
20
|
+
|
21
|
+
include BitStream
|
22
|
+
|
23
|
+
fields do
|
24
|
+
array :a1, nil, :unsigned_int, 16
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
class VarlenArraySample
|
30
|
+
|
31
|
+
include BitStream
|
32
|
+
|
33
|
+
fields do
|
34
|
+
array :a1, nil, :cstring
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
19
39
|
class TestArray < Test::Unit::TestCase
|
20
40
|
|
21
41
|
def setup
|
22
42
|
@spec = ArraySample.create "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
|
43
|
+
@spec_inf = InfiniteArraySample.create "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
|
44
|
+
@spec_var = VarlenArraySample.create "foo\0quux\0baz\0"
|
23
45
|
end
|
24
46
|
|
25
47
|
def test_a1
|
48
|
+
assert_equal(5, @spec.a1.size)
|
26
49
|
assert_equal(0x0102, @spec.a1[0])
|
27
50
|
assert_equal(0x0304, @spec.a1[1])
|
28
51
|
assert_equal(0x0506, @spec.a1[2])
|
29
52
|
assert_equal(0x0708, @spec.a1[3])
|
30
53
|
assert_equal(0x090a, @spec.a1[4])
|
54
|
+
assert_equal(nil, @spec.a1[5])
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_infinite
|
58
|
+
assert_equal(5, @spec_inf.a1.size)
|
59
|
+
assert_equal(0x0102, @spec_inf.a1[0])
|
60
|
+
assert_equal(0x0304, @spec_inf.a1[1])
|
61
|
+
assert_equal(0x0506, @spec_inf.a1[2])
|
62
|
+
assert_equal(0x0708, @spec_inf.a1[3])
|
63
|
+
assert_equal(0x090a, @spec_inf.a1[4])
|
64
|
+
assert_equal(nil, @spec_inf.a1[5])
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_varlen
|
68
|
+
assert_equal(3, @spec_var.a1.size)
|
69
|
+
assert_equal("foo", @spec_var.a1[0])
|
70
|
+
assert_equal("quux", @spec_var.a1[1])
|
71
|
+
assert_equal("baz", @spec_var.a1[2])
|
72
|
+
assert_equal(nil, @spec_var.a1[3])
|
31
73
|
end
|
32
74
|
|
33
75
|
end
|
data/test/test-dynarray.rb
CHANGED
File without changes
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Author:: Natsuki Kawai (natsuki.kawai@gmail.com)
|
2
|
+
# Copyright:: Copyright (c) 2012 Natsuki Kawai
|
3
|
+
# License:: 2-clause BSDL or Ruby's
|
4
|
+
|
5
|
+
require 'test/unit'
|
6
|
+
require 'lazy-string'
|
7
|
+
|
8
|
+
class TestLazyString < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@sample = LazyString.new
|
12
|
+
@sample << "foo"
|
13
|
+
@sample << "bar"
|
14
|
+
@sample << "quux"
|
15
|
+
@sample << "baz"
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_head
|
19
|
+
assert_equal("fo", @sample[0, 2].to_str)
|
20
|
+
assert_equal("foo", @sample[0, 3].to_str)
|
21
|
+
assert_equal("foob", @sample[0, 4].to_str)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_im
|
25
|
+
assert_equal("a", @sample[4, 1].to_str)
|
26
|
+
assert_equal("bar", @sample[3, 3].to_str)
|
27
|
+
assert_equal("obarq", @sample[2, 5].to_str)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_last
|
31
|
+
assert_equal("az", @sample[11, 2].to_str)
|
32
|
+
assert_equal("baz", @sample[10, 3].to_str)
|
33
|
+
assert_equal("xbaz", @sample[9, 4].to_str)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Author:: Natsuki Kawai (natsuki.kawai@gmail.com)
|
2
|
+
# Copyright:: Copyright (c) 2012 Natsuki Kawai
|
3
|
+
# License:: 2-clause BSDL or Ruby's
|
4
|
+
|
5
|
+
require 'test/unit'
|
6
|
+
require 'bitstream'
|
7
|
+
|
8
|
+
class OverloadSample1
|
9
|
+
|
10
|
+
include BitStream
|
11
|
+
|
12
|
+
fields do
|
13
|
+
unsigned :a, 32
|
14
|
+
unsigned :a, 32
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class OverloadSample2
|
20
|
+
|
21
|
+
include BitStream
|
22
|
+
|
23
|
+
fields do
|
24
|
+
unsigned :a, 8
|
25
|
+
if a == 1
|
26
|
+
cstring :a
|
27
|
+
else
|
28
|
+
string :a, 1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
class OverloadSampleArray
|
35
|
+
|
36
|
+
include BitStream
|
37
|
+
|
38
|
+
fields do
|
39
|
+
array :a, 3, :unsigned, 8
|
40
|
+
unsigned :a, 16
|
41
|
+
unsigned :b, 16
|
42
|
+
array :b, 2, :unsigned, 8
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
class TestOverload < Test::Unit::TestCase
|
48
|
+
|
49
|
+
def setup
|
50
|
+
@spec1 = OverloadSample1.create "\x00\x00\x00\x01\x00\x00\x00\x02"
|
51
|
+
@spec2 = OverloadSample2.create "\x01foo\0"
|
52
|
+
@spec_array = OverloadSampleArray.create "abc\x01\x01\x02\x02\x03\x03"
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_a1
|
56
|
+
assert_equal(2, @spec1.a)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_a2
|
60
|
+
assert_equal("foo", @spec2.a)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_array
|
64
|
+
assert_equal(0x0101, @spec_array.a)
|
65
|
+
assert_equal([3, 3], @spec_array.b)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
data/test/test-primitives.rb
CHANGED
@@ -13,12 +13,12 @@ class SimpleIntBe
|
|
13
13
|
byte_order :big_endian
|
14
14
|
|
15
15
|
fields {
|
16
|
-
|
17
|
-
|
18
|
-
cstring
|
19
|
-
|
20
|
-
|
21
|
-
string
|
16
|
+
unsigned "u1", 32
|
17
|
+
uint32 :u2
|
18
|
+
cstring "cs1"
|
19
|
+
unsigned "u3", 1
|
20
|
+
uint7 "u4"
|
21
|
+
string :s1, 3
|
22
22
|
}
|
23
23
|
|
24
24
|
end
|
data/test/test-suite.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Author:: Natsuki Kawai (natsuki.kawai@gmail.com)
|
2
|
-
# Copyright:: Copyright 2011 Natsuki Kawai
|
2
|
+
# Copyright:: Copyright (c) 2011, 2012 Natsuki Kawai
|
3
3
|
# License:: 2-clause BSDL or Ruby's
|
4
4
|
|
5
5
|
|
@@ -7,6 +7,7 @@ require 'test/test-primitives'
|
|
7
7
|
require 'test/test-array'
|
8
8
|
require 'test/test-dynarray'
|
9
9
|
require 'test/test-nesting'
|
10
|
+
require 'test/test-overload'
|
10
11
|
require 'test/test-simple-properties'
|
11
12
|
require 'test/types/test-character'
|
12
13
|
require 'test/types/test-integer'
|
metadata
CHANGED
@@ -1,89 +1,107 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: bitstream
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
6
10
|
platform: ruby
|
7
|
-
authors:
|
11
|
+
authors:
|
8
12
|
- Natsuki Kawai
|
9
13
|
autorequire:
|
10
14
|
bindir: bin
|
11
15
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
|
17
|
+
date: 2012-02-12 00:00:00 +09:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
15
21
|
name: random-accessible
|
16
|
-
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
17
24
|
none: false
|
18
|
-
requirements:
|
19
|
-
- -
|
20
|
-
- !ruby/object:Gem::Version
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
- 2
|
31
|
+
- 0
|
21
32
|
version: 0.2.0
|
22
33
|
type: :runtime
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
as picture, music, movie files, and e.t.c.. You can refer contents of bit streams
|
27
|
-
even when you are defining the data structures. With the function, you can write
|
28
|
-
a data structure easily that the header contains the data length of the body field.
|
34
|
+
version_requirements: *id001
|
35
|
+
description: |
|
36
|
+
BitStream is a mixin to write data structures of bit streams such as picture, music, movie files, and e.t.c.. You can refer contents of bit streams even when you are defining the data structures. With the function, you can write a data structure easily that the header contains the data length of the body field.
|
29
37
|
|
30
|
-
'
|
31
38
|
email: natsuki.kawai@gmail.com
|
32
39
|
executables: []
|
40
|
+
|
33
41
|
extensions: []
|
34
|
-
|
42
|
+
|
43
|
+
extra_rdoc_files:
|
35
44
|
- README.en
|
36
|
-
files:
|
45
|
+
files:
|
37
46
|
- lib/bitstream.rb
|
47
|
+
- lib/lazy-string.rb
|
38
48
|
- lib/types/string.rb
|
39
|
-
- lib/types/
|
40
|
-
- lib/types/character.rb
|
49
|
+
- lib/types/cstring.rb
|
41
50
|
- lib/types/string-utils.rb
|
51
|
+
- lib/types/character.rb
|
42
52
|
- lib/types/integer.rb
|
43
|
-
-
|
44
|
-
- lib/types/basetype.rb
|
53
|
+
- test/test-overload.rb
|
45
54
|
- test/test-nesting.rb
|
46
55
|
- test/test-dynarray.rb
|
47
|
-
- test/test-condition.rb
|
48
|
-
- test/test-simple-properties.rb
|
49
56
|
- test/test-suite.rb
|
50
|
-
- test/test-array.rb
|
51
57
|
- test/test-primitives.rb
|
52
|
-
- test/
|
53
|
-
- test/
|
54
|
-
- test/
|
58
|
+
- test/test-simple-properties.rb
|
59
|
+
- test/test-condition.rb
|
60
|
+
- test/test-lazy-string.rb
|
61
|
+
- test/test-array.rb
|
55
62
|
- test/types/test-string.rb
|
56
63
|
- test/types/test-integer.rb
|
57
|
-
-
|
64
|
+
- test/types/test-character.rb
|
65
|
+
- test/types/test-cstring.rb
|
58
66
|
- sample/flac.rb
|
67
|
+
- sample/gzip.rb
|
59
68
|
- sample/gzip-viewer.rb
|
60
|
-
- sample/front.rb
|
69
|
+
- sample/flac-front.rb
|
61
70
|
- README.en
|
71
|
+
has_rdoc: true
|
62
72
|
homepage: https://github.com/natsuki14/bitstream
|
63
|
-
licenses:
|
73
|
+
licenses:
|
64
74
|
- Ruby's
|
65
75
|
- 2-clause BSDL
|
66
76
|
post_install_message:
|
67
77
|
rdoc_options: []
|
68
|
-
|
78
|
+
|
79
|
+
require_paths:
|
69
80
|
- lib
|
70
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
82
|
none: false
|
72
|
-
requirements:
|
73
|
-
- -
|
74
|
-
- !ruby/object:Gem::Version
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
segments:
|
87
|
+
- 1
|
88
|
+
- 9
|
89
|
+
- 1
|
75
90
|
version: 1.9.1
|
76
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
92
|
none: false
|
78
|
-
requirements:
|
79
|
-
- -
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
version: "0"
|
82
99
|
requirements: []
|
100
|
+
|
83
101
|
rubyforge_project:
|
84
|
-
rubygems_version: 1.
|
102
|
+
rubygems_version: 1.3.7
|
85
103
|
signing_key:
|
86
104
|
specification_version: 3
|
87
105
|
summary: A bitstream parser supports dynamic-defined fields
|
88
|
-
test_files:
|
106
|
+
test_files:
|
89
107
|
- test/test-suite.rb
|
data/lib/types/basetype.rb
DELETED
data/lib/types/utils.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
module BitStream
|
2
|
-
|
3
|
-
module Utils
|
4
|
-
|
5
|
-
def self.align(s, offset, length)
|
6
|
-
index = offset
|
7
|
-
bitoffset = offset % 8
|
8
|
-
aligned = nil
|
9
|
-
if bitoffset != 0
|
10
|
-
aligned = ""
|
11
|
-
while index < offset + length
|
12
|
-
i = index / 8
|
13
|
-
c = s[i..(i + 1)].unpack('n')[0] >> (8 - bitoffset)
|
14
|
-
aligned << [c].pack('C')
|
15
|
-
index += 8
|
16
|
-
end
|
17
|
-
else
|
18
|
-
aligned = s[(offset / 8)...((offset + length) / 8)]
|
19
|
-
end
|
20
|
-
return aligned
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
|
-
|
data/test/types/test-utils.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'test/unit'
|
2
|
-
require 'types/utils'
|
3
|
-
|
4
|
-
class TestUtils < Test::Unit::TestCase
|
5
|
-
|
6
|
-
def test_align_aligned_bytelen
|
7
|
-
source = "abcde"
|
8
|
-
aligned = BitStream::Utils.align(source, 8, 8 * 3)
|
9
|
-
assert_equal("bcd", aligned)
|
10
|
-
end
|
11
|
-
|
12
|
-
def test_align_aligned_bytelen_lastbyte
|
13
|
-
source = "abcde"
|
14
|
-
aligned = BitStream::Utils.align(source, 8 * 2, 8 * 3)
|
15
|
-
assert_equal("cde", aligned)
|
16
|
-
end
|
17
|
-
|
18
|
-
def test_align_aligned
|
19
|
-
source = "defghi"
|
20
|
-
aligned = BitStream::Utils.align(source, 8 * 2, 8 * 2 + 6)
|
21
|
-
assert_equal(3, aligned.size)
|
22
|
-
aligned = aligned.unpack("a2C")
|
23
|
-
assert_equal("fg", aligned[0])
|
24
|
-
assert_equal("h".ord & 0b11111100, aligned[1] & 0b11111100)
|
25
|
-
end
|
26
|
-
|
27
|
-
def test_align_aligned_lastbyte
|
28
|
-
source = "defghi"
|
29
|
-
aligned = BitStream::Utils.align(source, 8 * 3, 8 * 2 + 6)
|
30
|
-
assert_equal(3, aligned.size)
|
31
|
-
aligned = aligned.unpack("a2C")
|
32
|
-
assert_equal("gh", aligned[0])
|
33
|
-
assert_equal("i".ord & 0b11111100, aligned[1] & 0b11111100)
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|