bitstream 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.en ADDED
@@ -0,0 +1,71 @@
1
+ = BitStream
2
+
3
+ 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.
4
+
5
+ == Installation
6
+
7
+ gem install bitstream
8
+
9
+ == Sample
10
+
11
+ gzip.rb: A metadata definition of a gzip file.
12
+ ( http://www.gzip.org/zlib/rfc-gzip.html )
13
+
14
+ require 'bitstream'
15
+
16
+ class Gzip
17
+
18
+ include BitStream
19
+ byte_order :little_endian
20
+
21
+ FHCRC = 1 << 1
22
+ FEXTRA = 1 << 2
23
+ FNAME = 1 << 3
24
+ FCOMMENT = 1 << 4
25
+
26
+ fields do
27
+ unsigned :id1, 8
28
+ unsigned :id2, 8
29
+ unsigned :cm, 8
30
+ unsigned :flg, 8
31
+ unsigned :mtime, 32
32
+ unsigned :xfl, 8
33
+ unsigned :os, 8
34
+ if (flg & FEXTRA) != 0
35
+ unsigned :xlen, 16
36
+ string :extra_field, xlen
37
+ end
38
+ if (flg & FNAME) != 0
39
+ # cstring means a NULL-terminated string.
40
+ cstring :original_file_name
41
+ end
42
+ if (flg & FCOMMENT) != 0
43
+ # cstring means a NULL-terminated string.
44
+ cstring :file_comment
45
+ end
46
+ if (flg & FHCRC) != 0
47
+ unsigned :crc16, 16
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+ gzip-viewer.rb: A viewer of the original file name of a gzip file.
54
+
55
+ require_relative 'gzip'
56
+
57
+ gzip = nil
58
+ File.open(ARGV[0], "rb") do |file|
59
+ gzip = Gzip.create(file.read)
60
+ end
61
+
62
+ if gzip.respond_to? :original_file_name
63
+ puts "original_file_name:#{gzip.original_file_name}"
64
+ else
65
+ puts "The gzip does not contain its original file name."
66
+ end
67
+
68
+ == License
69
+
70
+ 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.
71
+ Copyright (c) 2011, Natsuki Kawai.
data/lib/bitstream.rb ADDED
@@ -0,0 +1,454 @@
1
+ # Author:: Natsuki Kawai (natsuki.kawai@gmail.com)
2
+ # Copyright:: Copyright 2011 Natsuki Kawai
3
+ # License:: 2-clause BSDL or Ruby's
4
+
5
+ require 'random-accessible'
6
+
7
+ require 'types/integer'
8
+ require 'types/string'
9
+ require 'types/cstring'
10
+ require 'types/character'
11
+
12
+ module BitStream
13
+
14
+ class BitStreamError < Exception
15
+ end
16
+
17
+ Properties = Struct.new(
18
+ :curr_offset, :fields, :mode, :raw_data,
19
+ :initial_offset, :user_props, :eval_queue
20
+ )
21
+
22
+ class ArrayProxy
23
+
24
+ include RandomAccessible
25
+
26
+ def initialize(instance)
27
+ @fields = []
28
+ @values = []
29
+ @updated = []
30
+ @instance = instance
31
+ @size = 0
32
+ end
33
+
34
+ def add_field(field)
35
+ @fields << field
36
+ @size += 1
37
+ end
38
+
39
+ def get_field(pos)
40
+ @fields[pos]
41
+ end
42
+
43
+ def read_access(pos)
44
+ unless @updated[pos]
45
+ field = @fields[pos]
46
+ unless field.has_read?
47
+ BitStream.read_one_field(field, @instance)
48
+ end
49
+ @values[pos] = field.value
50
+ @fields[pos] = nil
51
+ @updated[pos] = true
52
+ end
53
+ return @values[pos]
54
+ end
55
+
56
+ def write_access(pos, val)
57
+ @fields[pos] = nil
58
+ @values[pos] = val
59
+ @updated[pos] = true
60
+ end
61
+
62
+ def shrink(n)
63
+ @fields.pop(n)
64
+ @size -= n
65
+ dif = @values.size - @size
66
+ if dif > 0
67
+ @values.pop(dif)
68
+ @updated.pop(dif)
69
+ end
70
+ end
71
+
72
+ attr_reader :size
73
+
74
+ end
75
+
76
+ # TODO: Use data structure that enqueues and dequeues in O(1).
77
+ class Queue < Array
78
+
79
+ alias :enq :push
80
+ alias :deq :shift
81
+
82
+ end
83
+
84
+ class NestWrapper
85
+
86
+ def initialize(type, inherited_props, user_props)
87
+ @type = type
88
+ # TODO: Implement priority between the properties.
89
+ @props = user_props.merge(inherited_props)
90
+ end
91
+
92
+ def length
93
+ # TODO: Implement me.
94
+ nil
95
+ end
96
+
97
+ def read(s, offset)
98
+ instance = @type.create_with_offset(s, offset, @props)
99
+ [instance, instance.length]
100
+ end
101
+
102
+ #def write(s, offset, data)
103
+ # TODO: Implement me.
104
+ #end
105
+ end
106
+
107
+ module Utils
108
+
109
+ def self.class2symbol(type)
110
+ name = type.name.split("::").last
111
+ name = self.camel2snake(name).intern
112
+ end
113
+
114
+ def self.camel2snake(camel)
115
+ snake = camel.dup
116
+ snake[0] = snake[0].downcase
117
+ snake.gsub(/[A-Z]/) do |s|
118
+ "_" + s.downcase
119
+ end
120
+ end
121
+
122
+ end
123
+
124
+ def self.read_one_field(value, instance)
125
+ props = instance.bitstream_properties
126
+ queue = props.eval_queue
127
+
128
+ while value.offset.nil?
129
+ field = queue.deq
130
+ field.offset = props.curr_offset
131
+ length = field.length
132
+ length = field.decide_length if length.nil?
133
+ props.curr_offset += length
134
+ end
135
+ value.read
136
+ end
137
+
138
+ def self.index_all_fields(instance)
139
+ props = instance.bitstream_properties
140
+ queue = props.eval_queue
141
+
142
+ queue.each do |field|
143
+ field.offset = props.curr_offset
144
+ length = field.length
145
+ length = field.decide_length if length.nil?
146
+ props.curr_offset += length
147
+ end
148
+ queue.clear
149
+ end
150
+
151
+ class Value
152
+
153
+ def initialize(type, raw_data)
154
+ @type = type
155
+ @raw_data = raw_data
156
+ @length = @type.length if @type.respond_to? :length
157
+ @has_read = false
158
+ end
159
+
160
+ def has_read?
161
+ @has_read
162
+ end
163
+
164
+ def read
165
+ unless @has_read
166
+ if @offset.nil?
167
+ raise "Has not been set offset."
168
+ else
169
+ @value, @length = @type.read(@raw_data, @offset)
170
+ @has_read = true
171
+ end
172
+ end
173
+ return @value
174
+ end
175
+
176
+ def decide_length
177
+ # @length must not be nil if @has_read.
178
+ if @length.nil?
179
+ if @offset.nil?
180
+ raise "Has not been set offset."
181
+ else
182
+ @value, @length = @type.read(@raw_data, @offset)
183
+ @has_read = true
184
+ end
185
+ end
186
+ return @length
187
+ end
188
+
189
+ attr_reader :length, :value
190
+ attr_accessor :offset
191
+
192
+ end
193
+
194
+ module ClassMethods
195
+
196
+ def initialize_for_class_methods(types)
197
+ @field_defs = []
198
+ @fields = {}
199
+ @types = types.dup
200
+ @index = 0
201
+ @class_props = {}
202
+ @bitstream_mutex = Mutex.new
203
+ end
204
+
205
+ def fields(&field_def)
206
+ @field_defs << field_def
207
+ end
208
+
209
+ def initialize_instance(raw_data, instance)
210
+ props = instance.bitstream_properties
211
+ props.mode = :field_def
212
+ props.user_props.merge!(@class_props)
213
+ @bitstream_mutex.synchronize do
214
+ @instance = instance
215
+
216
+ @field_defs.each do |field_def|
217
+ field_def.call
218
+ end
219
+ end
220
+ end
221
+
222
+
223
+ def self.types
224
+ @types
225
+ end
226
+
227
+ def self.register_types(types)
228
+ types.each do |t|
229
+ register_type(t, nil)
230
+ end
231
+ end
232
+
233
+ def self.register_type(type, name = nil)
234
+ if name.nil?
235
+ name = Utils.class2symbol type
236
+ end
237
+
238
+ @types = {} if @types.nil?
239
+ @types[name] = type
240
+
241
+ add_type(type, name, self)
242
+ end
243
+
244
+ def self.alias_type(alias_name, aliasee)
245
+ @types[alias_name] = @types[aliasee]
246
+ alias_method(alias_name, aliasee)
247
+ end
248
+
249
+ def props
250
+ @instance.bitstream_properties.user_props
251
+ end
252
+
253
+ def byte_order(order)
254
+ @class_props[:byte_order] = order.intern
255
+ end
256
+
257
+ def self.add_type(type, name = nil, bs = self)
258
+ bs.instance_eval do
259
+ define_method(name) do |*args|
260
+ name = args.shift.intern
261
+ #if respond_to? name
262
+ # raise "#{name} has already defined."
263
+ #end
264
+
265
+ props = @instance.bitstream_properties
266
+ fields = props.fields
267
+ queue = props.eval_queue
268
+ user_props = props.user_props
269
+
270
+ case props.mode
271
+ when :field_def
272
+ if type.respond_to? :read
273
+ type_instance = type
274
+ else
275
+ type_instance = type.instance(user_props, *args)
276
+ end
277
+ field = Value.new(type_instance, props.raw_data)
278
+ queue.enq(field)
279
+ @instance.bitstream_properties.fields[name] = field
280
+
281
+ name_in_method = name
282
+
283
+ define_method name do
284
+ field = bitstream_properties.fields[name_in_method]
285
+ if field.value.nil?
286
+ BitStream.read_one_field(field, self)
287
+ end
288
+ field.value
289
+ end
290
+
291
+ instance = @instance
292
+ singleton_class.instance_eval do
293
+ define_method name_in_method do
294
+ instance.send(name_in_method)
295
+ end
296
+ end
297
+ end
298
+ end
299
+ end
300
+ end
301
+
302
+ def array(name, size, type_name, *type_args)
303
+ name = name.intern
304
+ type_name = type_name.intern
305
+ type = @types[type_name]
306
+ props = @instance.bitstream_properties
307
+ queue = props.eval_queue
308
+ user_props = props.user_props
309
+
310
+ if type.nil?
311
+ raise BitStreamError, "There is no type named \"#{type_name}\""
312
+ end
313
+
314
+ if type.respond_to? :read
315
+ unless type_args.empty?
316
+ raise BitStreamError, "#{type} does not accept any arguments."
317
+ end
318
+ type_instance = type
319
+ else
320
+ type_instance = type.instance(user_props, *type_args)
321
+ end
322
+
323
+ case props.mode
324
+ when :field_def
325
+ field = ArrayProxy.new(@instance)
326
+ size.times do
327
+ field_element = Value.new(type_instance, props.raw_data)
328
+ field.add_field(field_element)
329
+ queue.enq(field_element)
330
+ end
331
+
332
+ define_method name do
333
+ field
334
+ end
335
+
336
+ name_in_method = name
337
+ instance = @instance
338
+ singleton_class.instance_eval do
339
+ define_method name do
340
+ instance.send(name_in_method)
341
+ end
342
+ end
343
+ end
344
+ end
345
+
346
+ def dyn_array(name, type_name, *type_args)
347
+ name = name.intern
348
+ type_name = type_name.intern
349
+ type = @types[type_name]
350
+ props = @instance.bitstream_properties
351
+ fields = props.fields
352
+ queue = props.eval_queue
353
+ user_props = props.user_props
354
+
355
+ if type.nil?
356
+ raise BitStreamError, "There is no type named \"#{type_name}\""
357
+ end
358
+
359
+ if type.respond_to? :read
360
+ unless type_args.empty?
361
+ raise BitStreamError, "#{type} does not accept any arguments."
362
+ end
363
+ type_instance = type
364
+ else
365
+ type_instance = type.instance(user_props, *type_args)
366
+ end
367
+
368
+ case props.mode
369
+ when :field_def
370
+ if fields[name].nil?
371
+ fields[name] = ArrayProxy.new(@instance)
372
+ end
373
+ field = Value.new(type_instance, props.raw_data)
374
+ fields[name].add_field(field)
375
+ queue.enq(field)
376
+
377
+ name_in_method = name
378
+
379
+ define_method name do
380
+ return fields[name_in_method]
381
+ end
382
+
383
+ instance = @instance
384
+ singleton_class.instance_eval do
385
+ define_method name do
386
+ instance.send(name_in_method)
387
+ end
388
+ end
389
+ end
390
+ end
391
+
392
+ def add_type(type, name = nil)
393
+ if name.nil?
394
+ name = Utils.class2symbol(type)
395
+ end
396
+ @types[name] = type
397
+ ClassMethods.add_type(type, name, self.singleton_class)
398
+ end
399
+
400
+ register_types [Unsigned, Signed, Cstring, String, Char]
401
+ alias_type :unsigned_int, :unsigned
402
+ alias_type :int, :signed
403
+
404
+ def create(s, props = {})
405
+ create_with_offset(s, 0, props)
406
+ end
407
+
408
+ def create_with_offset(s, offset, props = {})
409
+ klass = Class.new(self)
410
+ instance = klass.new(s, offset)
411
+ instance.bitstream_properties.user_props = props
412
+ initialize_instance(s, instance)
413
+ return instance
414
+ end
415
+
416
+ #def method_missing(name, *args)
417
+ # TODO: Support methods like "int16" "uint1"
418
+ # super name, args
419
+ #end
420
+
421
+ def instance(inherited_props, user_props = {})
422
+ NestWrapper.new(self, inherited_props, user_props)
423
+ end
424
+
425
+ end
426
+
427
+ def self.included(obj)
428
+ obj.extend ClassMethods
429
+ obj.initialize_for_class_methods(ClassMethods.types)
430
+ end
431
+
432
+ def initialize(s, offset = 0)
433
+ props = Properties.new
434
+ props.curr_offset = offset
435
+ props.fields = {}
436
+ props.raw_data = s
437
+ props.initial_offset = offset
438
+ props.eval_queue = Queue.new
439
+ @bitstream_properties = props
440
+ end
441
+
442
+ def length
443
+ BitStream.index_all_fields(self)
444
+ props = @bitstream_properties
445
+ props.curr_offset - props.initial_offset
446
+ end
447
+
448
+ #def properties=(props)
449
+ # Method to override.
450
+ #end
451
+
452
+ attr_accessor :bitstream_properties
453
+
454
+ end
@@ -0,0 +1,35 @@
1
+ # Author:: Natsuki Kawai (natsuki.kawai@gmail.com)
2
+ # Copyright:: Copyright 2011 Natsuki Kawai
3
+ # License:: 2-clause BSDL or Ruby's
4
+
5
+
6
+ module BitStream
7
+
8
+ class Char
9
+
10
+ @instance = new
11
+
12
+ def self.instance(props)
13
+ @instance
14
+ end
15
+
16
+ def length
17
+ 8
18
+ end
19
+
20
+ def read(s, offset)
21
+ byteoffset = offset / 8
22
+ bitoffset = offset % 8
23
+
24
+ value = s[byteoffset]
25
+
26
+ return [value, 8]
27
+ end
28
+
29
+ def write(s, offset, value)
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
@@ -0,0 +1,40 @@
1
+ # Author:: Natsuki Kawai (natsuki.kawai@gmail.com)
2
+ # Copyright:: Copyright 2011 Natsuki Kawai
3
+ # License:: 2-clause BSDL or Ruby's
4
+
5
+
6
+ module BitStream
7
+
8
+ class Cstring
9
+
10
+ @instance = new
11
+
12
+ def self.instance(props)
13
+ @instance
14
+ end
15
+
16
+ def length
17
+ nil
18
+ end
19
+
20
+ def read(s, offset)
21
+ byteindex = offset / 8
22
+ bitindex = offset % 8
23
+ val = ""
24
+ begin
25
+ byte = s[byteindex].unpack('C')[0]
26
+ val << byte
27
+ byteindex += 1
28
+ end while byte != 0
29
+
30
+ bytelen = val.size
31
+ val.slice!(val.size - 1)
32
+ return [val, 8 * bytelen]
33
+ end
34
+
35
+ def write(s, offset, data)
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,131 @@
1
+ # Author:: Natsuki Kawai (natsuki.kawai@gmail.com)
2
+ # Copyright:: Copyright 2011 Natsuki Kawai
3
+ # License:: 2-clause BSDL or Ruby's
4
+
5
+
6
+ module BitStream
7
+
8
+ class Unsigned
9
+
10
+ @be_instances = Hash.new do |hash, key|
11
+ hash[key] = new(key, true)
12
+ end
13
+
14
+ @le_instances = Hash.new do |hash, key|
15
+ hash[key] = new(key, false)
16
+ end
17
+
18
+ BE_SYMBOLS = [:big_endian, :be, :msb_first, :motorola, nil]
19
+ LE_SYMBOLS = [:little_endian, :le, :lsb_first, :intel]
20
+
21
+ def self.instance(props, bit_width)
22
+ byte_order = props[:byte_order]
23
+ if BE_SYMBOLS.include?(byte_order)
24
+ @be_instances[bit_width]
25
+ elsif LE_SYMBOLS.include?(byte_order)
26
+ @le_instances[bit_width]
27
+ else
28
+ STDERR.puts("Unknown byte order #{byte_order.inspect}.",
29
+ "Assuming that the byte order is big endian.")
30
+ @be_instances[bit_width]
31
+ end
32
+ end
33
+
34
+ attr_reader :bit_width
35
+
36
+ def initialize(bit_width, big_endian)
37
+ @bit_width = bit_width
38
+ @big_endian = big_endian
39
+ end
40
+
41
+ def length
42
+ @bit_width
43
+ end
44
+
45
+ def read(s, offset)
46
+ value = 0
47
+ byteoffset = offset / 8
48
+ bitoffset = offset % 8
49
+ bytelength = (@bit_width + bitoffset + 7) / 8
50
+
51
+ bytes = s[byteoffset, bytelength].unpack('C*')
52
+ bytes.reverse! unless @big_endian
53
+
54
+ bytes.each do |b|
55
+ value <<= 8
56
+ value |= b
57
+ end
58
+
59
+ value &= ~(-1 << (bytelength * 8 - bitoffset))
60
+ value >>= (8 - (@bit_width + bitoffset) % 8) % 8
61
+
62
+ return [value, @bit_width]
63
+ end
64
+
65
+ def write(s, offset, value)
66
+ byteoffset = offset / 8
67
+ bitoffset = offset % 8
68
+
69
+ if bitoffset != 0
70
+ raise "#{self.class.name}#write has not supported non-byte-aligned fields yet."
71
+ end
72
+ unless @big_endian
73
+ raise "#{self.class.name}#write has not supported little endian yet."
74
+ end
75
+
76
+ i = 0
77
+ tail = ""
78
+ while value != 0
79
+ index = byteoffset + @bit_width / 8 + i - 1
80
+ if s.bytesize <= index
81
+ tail.insert(0, [value & 0xff].pack('C'))
82
+ else
83
+ s[index] = [value & 0xff].pack('C')
84
+ end
85
+ value >>= 8
86
+ i -= 1
87
+ end
88
+ s << tail
89
+
90
+ return @bit_width
91
+ end
92
+
93
+ end
94
+
95
+ class Signed
96
+
97
+ @instances = Hash.new do |hash, key|
98
+ hash[key] = new(key)
99
+ end
100
+
101
+ def self.instance(props, bit_width)
102
+ unsigned = Unsigned.instance(props, bit_width)
103
+ return @instances[unsigned]
104
+ end
105
+
106
+ def initialize(unsigned)
107
+ @unsigned = unsigned
108
+ end
109
+
110
+ def length
111
+ @unsigned.length
112
+ end
113
+
114
+ def read(s, offset)
115
+ val, len = @unsigned.read(s, offset)
116
+ mask = -1 << (len - 1)
117
+ if (val & mask) != 0
118
+ val |= mask
119
+ end
120
+ return [val, len]
121
+ end
122
+
123
+ def write(s, offset, value)
124
+ mask = ~(-1 << len)
125
+ value &= mask
126
+ return @unsigned.write(s, offset, value)
127
+ end
128
+
129
+ end
130
+
131
+ end