bitstream 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.en +71 -0
- data/lib/bitstream.rb +454 -0
- data/lib/types/character.rb +35 -0
- data/lib/types/cstring.rb +40 -0
- data/lib/types/integer.rb +131 -0
- data/lib/types/string-utils.rb +37 -0
- data/lib/types/string.rb +52 -0
- data/sample/gzip-viewer.rb +12 -0
- data/sample/gzip.rb +38 -0
- data/sample/tar-viewer.rb +15 -0
- data/test/test-array.rb +33 -0
- data/test/test-condition.rb +50 -0
- data/test/test-dynarray.rb +34 -0
- data/test/test-nesting.rb +64 -0
- data/test/test-primitives.rb +92 -0
- data/test/test-simple-properties.rb +34 -0
- data/test/test-suite.rb +14 -0
- data/test/types/test-character.rb +19 -0
- data/test/types/test-cstring.rb +18 -0
- data/test/types/test-integer.rb +98 -0
- data/test/types/test-string.rb +41 -0
- metadata +85 -0
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
|