binary_parser 1.1.1 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f00ee4cb710d229fe1bd23f7d2dd5b9d2e63fe17
4
- data.tar.gz: 50a4f28986bdc26f158531dcbda7040cdd16cd9a
3
+ metadata.gz: fc0580a7a3667f74608fc328897ce3ee3772b94a
4
+ data.tar.gz: d9c1e1b204e9813ff8239cd2d36f45a509430c54
5
5
  SHA512:
6
- metadata.gz: f24c616c42e6a4b2866c18d28764f2885758a566d1df136118a0c9fcfb91dece7ec573e5a560a3f6655551e29641dd2bee5e8bc8a6da527660db30521a83e100
7
- data.tar.gz: 666608cd8a53767641515c5164a9916cb537db71244e148da052831ed265920be5925d343c924ddb5703d297dd88601ac4453d0ac72e26446fa320d30f23c34a
6
+ metadata.gz: 658d10fb088312e15f51965c4a8a9f366b60a58e3be00a61d0dc70fc243f0f124e8adac9b5ef5f113eb2e43ca7b0fa7f83b0998684a7001492819cec4f94547e
7
+ data.tar.gz: c8347f938c25674e959014f1bcc1f4097a6d8efdde741f819a5c72f490297d372415c70c68121eea7a14cb6cfffab3a24f46ba394a64163472548ec6a8dc850e
data/.gitignore CHANGED
@@ -11,6 +11,7 @@ coverage
11
11
  doc/
12
12
  lib/bundler/man
13
13
  pkg
14
+ test
14
15
  rdoc
15
16
  spec/reports
16
17
  test/tmp
@@ -7,10 +7,10 @@ require 'binary_parser/version'
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = "binary_parser"
9
9
  spec.version = BinaryParser::VERSION
10
- spec.authors = ["rokugats(u)"]
10
+ spec.authors = ["sawaken"]
11
11
  spec.email = ["sasasawada@gmail.com"]
12
12
  spec.summary = "An elegant DSL library for parsing binary-data."
13
- spec.homepage = "https://github.com/rokugats/ruby-binary-parser"
13
+ spec.homepage = "https://github.com/sawaken/ruby-binary-parser"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
@@ -1,3 +1,3 @@
1
1
  module BinaryParser
2
- VERSION = "1.1.1"
2
+ VERSION = "1.1.2"
3
3
  end
data/lib/binary_parser.rb CHANGED
@@ -12,7 +12,10 @@ module BinaryParser
12
12
  'expression.rb',
13
13
  'bit_position.rb',
14
14
  'condition.rb',
15
- 'buffered_stream.rb'
15
+ 'free_condition.rb',
16
+ 'buffered_stream.rb',
17
+ 'proxy.rb',
18
+ 'memorize.rb'
16
19
  ]
17
20
 
18
21
  GENERAL_CLASS_FILES.each do |path|
@@ -38,6 +41,7 @@ module BinaryParser
38
41
  LIB_DIR = '/lib/'
39
42
  LIB_FILES =
40
43
  ['loop_list.rb',
44
+ 'while_list.rb',
41
45
  'scope.rb',
42
46
  'structure_definition.rb',
43
47
  'template_base.rb',
@@ -6,6 +6,20 @@ module BinaryParser
6
6
  bytes = chars[0, 5].map{|i| sprintf("0x%02x", i)}.join(", ")
7
7
  return "[" + bytes + (chars.length > 5 ? ", ..." : "") + "]"
8
8
  end
9
+
10
+ def to_str
11
+ self.to_s
12
+ end
13
+
14
+ def ==(other)
15
+ if other.is_a?(Binary)
16
+ self.to_s == other.to_s
17
+ elsif other.is_a?(String)
18
+ self.to_s == other
19
+ else
20
+ super
21
+ end
22
+ end
9
23
  end
10
24
  end
11
25
  end
@@ -1,8 +1,81 @@
1
1
  module BinaryParser
2
2
  module BuiltInTemplate
3
3
  class UInt < TemplateBase
4
+
5
+ include Comparable
6
+
4
7
  def content_description
5
- to_i.to_s
8
+ self.to_i.to_s
9
+ end
10
+
11
+ def to_s(base=10)
12
+ self.to_i.to_s(base)
13
+ end
14
+
15
+ def [](bit_index)
16
+ self.to_i[bit_index]
17
+ end
18
+
19
+ def coerce(other)
20
+ if other.is_a?(Integer)
21
+ return other, self.to_i
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ def +(other)
28
+ if other.is_a?(UInt)
29
+ self.to_i + other.to_i
30
+ elsif other.is_a?(Integer)
31
+ self.to_i + other
32
+ else
33
+ x, y = other.coerce(self)
34
+ x + y
35
+ end
36
+ end
37
+
38
+ def *(other)
39
+ if other.is_a?(UInt)
40
+ self.to_i * other.to_i
41
+ elsif other.is_a?(Integer)
42
+ self.to_i * other
43
+ else
44
+ x, y = other.coerce(self)
45
+ x * y
46
+ end
47
+ end
48
+
49
+ def -(other)
50
+ if other.is_a?(UInt)
51
+ self.to_i - other.to_i
52
+ elsif other.is_a?(Integer)
53
+ self.to_i - other
54
+ else
55
+ x, y = other.coerce(self)
56
+ x - y
57
+ end
58
+ end
59
+
60
+ def /(other)
61
+ if other.is_a?(UInt)
62
+ self.to_i / other.to_i
63
+ elsif other.is_a?(Integer)
64
+ self.to_i / other
65
+ else
66
+ x, y = other.coerce(self)
67
+ x / y
68
+ end
69
+ end
70
+
71
+ def <=>(other)
72
+ if other.is_a?(UInt)
73
+ self.to_i <=> other.to_i
74
+ elsif other.is_a?(Integer)
75
+ self.to_i <=> other
76
+ else
77
+ nil
78
+ end
6
79
  end
7
80
  end
8
81
  end
@@ -12,7 +12,7 @@ module BinaryParser
12
12
  end
13
13
 
14
14
  def add_name(name)
15
- return BitPosition.new(@imm, @names + [name])
15
+ return BitPosition.new(@imm, @names + [Expression.length_var(name)])
16
16
  end
17
17
 
18
18
  def eval(&name_eval_block)
@@ -1,11 +1,12 @@
1
1
  module BinaryParser
2
2
  class Condition
3
3
  def initialize(*var_names, &condition_proc)
4
- @var_names, @condition_proc = var_names, condition_proc
4
+ @tokens = var_names.map{|symbol| Expression.value_var(symbol)}
5
+ @condition_proc = condition_proc
5
6
  end
6
7
 
7
- def eval(&name_eval_proc)
8
- return @condition_proc.call(*@var_names.map{|name| name_eval_proc.call(name)})
8
+ def eval(&token_eval_proc)
9
+ return @condition_proc.call(*@tokens.map{|token| token_eval_proc.call(token)})
9
10
  end
10
11
  end
11
12
  end
@@ -1,66 +1,242 @@
1
1
  module BinaryParser
2
2
  class Expression
3
- attr_reader :rpn
4
3
 
5
- def initialize(rpn)
6
- @rpn = rpn
4
+ def self.value_var(symbol)
5
+ Token::Variable::Value.new(symbol)
6
+ end
7
+
8
+ def self.length_var(symbol)
9
+ Token::Variable::Length.new(symbol)
10
+ end
11
+
12
+ def self.control_var(symbol)
13
+ Token::Variable::Control.new(symbol)
14
+ end
15
+
16
+ def self.nextbits_var(length)
17
+ Token::Variable::Nextbits.new(length)
18
+ end
19
+
20
+ def self.immediate(value)
21
+ Token::Immediate.new(value)
22
+ end
23
+
24
+ def value_var?
25
+ self.is_a?(Token::Variable::Value)
26
+ end
27
+
28
+ def length_var?
29
+ self.is_a?(Token::Variable::Length)
30
+ end
31
+
32
+ def control_var?
33
+ self.is_a?(Token::Variable::Control)
34
+ end
35
+
36
+ def nextbits_var?
37
+ self.is_a?(Token::Variable::Nextbits)
38
+ end
39
+
40
+ def immediate?
41
+ self.is_a?(Token::Immediate)
42
+ end
43
+
44
+ def coerce(other)
45
+ if other.is_a?(Integer)
46
+ return Token::Immediate.new(other), self
47
+ else
48
+ super
49
+ end
7
50
  end
8
51
 
9
52
  def +(other)
10
- return Expression.new(@rpn + to_rpn(other) + [:__add])
53
+ binary_op(other, Token::Operator::Add.instance)
11
54
  end
12
55
 
13
56
  def -(other)
14
- return Expression.new(@rpn + to_rpn(other) + [:__sub])
57
+ binary_op(other, Token::Operator::Sub.instance)
15
58
  end
16
59
 
17
60
  def *(other)
18
- return Expression.new(@rpn + to_rpn(other) + [:__mul])
61
+ binary_op(other, Token::Operator::Mul.instance)
19
62
  end
20
63
 
21
64
  def /(other)
22
- return Expression.new(@rpn + to_rpn(other) + [:__div])
65
+ binary_op(other, Token::Operator::Div.instance)
23
66
  end
24
67
 
25
- def to_rpn(other)
26
- case other
27
- when Integer
28
- return [other]
29
- when Expression
30
- return other.rpn
68
+ def %(other)
69
+ binary_op(other, Token::Operator::Mod.instance)
70
+ end
71
+
72
+ def binary_op(other, op)
73
+ BinaryOperator.new(self, other, op)
74
+ end
75
+
76
+ def to_exp(exp)
77
+ if exp.is_a?(Expression)
78
+ exp
79
+ elsif exp.is_a?(Integer)
80
+ Token::Immediate.new(exp)
31
81
  else
32
- raise BadManipulationError, "Unknown type of other (#{other.class})."
82
+ raise BadManipulationError, "Can't convert #{exp} into Expression."
83
+ end
84
+ end
85
+
86
+ def eval(&token_eval_proc)
87
+ to_rpn.eval(&token_eval_proc)
88
+ end
89
+
90
+ def variable_tokens
91
+ to_rpn.tokens.select{|token| token.is_a?(Token::Variable)}
92
+ end
93
+
94
+ def initialize(*args)
95
+ raise BadManipulationError, "Expression is abstract class."
96
+ end
97
+
98
+ class BinaryOperator < self
99
+ def initialize(chl, chr, op)
100
+ check_op(op)
101
+ @chl, @chr, @op = to_exp(chl), to_exp(chr), op
102
+ end
103
+
104
+ def check_op(op)
105
+ unless op.is_a?(Token::Operator)
106
+ raise BadManipulationError, "Argument should be Token::Operator."
107
+ end
108
+ end
109
+
110
+ def to_rpn
111
+ @rpn ||= @chl.to_rpn + @chr.to_rpn + @op.to_rpn
33
112
  end
34
113
  end
35
114
 
36
- def variables
37
- control_symbols = [:__add, :__sub, :__mul, :__div]
38
- return @rpn.select{|token| token.is_a?(Symbol) && !control_symbols.include?(token)}
39
- end
40
-
41
- def eval(&name_eval_block)
42
- stack, rpn = [], @rpn.dup
43
- until rpn.empty?
44
- stack << rpn.shift
45
- case stack.last
46
- when :__add
47
- arg = [stack.pop, stack.pop, stack.pop]
48
- stack << arg[2] + arg[1]
49
- when :__sub
50
- arg = [stack.pop, stack.pop, stack.pop]
51
- stack << arg[2] - arg[1]
52
- when :__mul
53
- arg = [stack.pop, stack.pop, stack.pop]
54
- stack << arg[2] * arg[1]
55
- when :__div
56
- arg = [stack.pop, stack.pop, stack.pop]
57
- stack << arg[2] / arg[1]
58
- when Symbol
59
- stack << name_eval_block.call(stack.pop)
115
+ class Token < self
116
+
117
+ def initialize
118
+ #override but do nothing
119
+ end
120
+
121
+ def to_rpn
122
+ @rpn ||= RPN.new(self)
123
+ end
124
+
125
+ class Variable < self
126
+
127
+ attr_reader :symbol
128
+
129
+ def initialize(symbol)
130
+ raise BadManipulationError, "Argument should be Symbol." unless symbol.is_a?(Symbol)
131
+ @symbol = symbol
132
+ end
133
+
134
+ class Length < self
135
+
136
+ end
137
+
138
+ class Value < self
139
+
140
+ end
141
+
142
+ class Control < self
143
+
144
+ end
145
+
146
+ class Nextbits < self
147
+
148
+ attr_reader :length
149
+
150
+ def initialize(length)
151
+ unless length.is_a?(Integer) && length > 0
152
+ raise BadManipulationError, "Argument should be positive Integer."
153
+ end
154
+ @length = length
155
+ end
156
+ end
157
+ end
158
+
159
+ class Immediate < self
160
+
161
+ attr_reader :value
162
+
163
+ def initialize(value)
164
+ raise BadManipulationError, "Argument should be Integer." unless value.is_a?(Integer)
165
+ @value = value
166
+ end
167
+ end
168
+
169
+ class Operator < self
170
+
171
+ require 'singleton'
172
+ include Singleton
173
+
174
+ class Add < self
175
+ def operate(op1, op2)
176
+ op1 + op2
177
+ end
178
+ end
179
+
180
+ class Sub < self
181
+ def operate(op1, op2)
182
+ op1 - op2
183
+ end
184
+ end
185
+
186
+ class Mul < self
187
+ def operate(op1, op2)
188
+ op1 * op2
189
+ end
190
+ end
191
+
192
+ class Div < self
193
+ def operate(op1, op2)
194
+ op1 / op2
195
+ end
196
+ end
197
+
198
+ class Mod < self
199
+ def operate(op1, op2)
200
+ op1 % op2
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ class RPN
207
+ attr_reader :tokens
208
+
209
+ def initialize(*tokens)
210
+ check_tokens(tokens)
211
+ @tokens = tokens
212
+ end
213
+
214
+ def check_tokens(tokens)
215
+ tokens.all?{|token| token.is_a?(Expression::Token)}
216
+ end
217
+
218
+ def +(other)
219
+ RPN.new(*(self.tokens + other.tokens))
220
+ end
221
+
222
+ def eval(&token_eval_proc)
223
+ stack = @tokens.inject([]) do |stack, token|
224
+ if token.is_a?(Expression::Token::Operator)
225
+ raise BadManipulationError, "Cannot calculate this RPN." if stack.length < 2
226
+ stack + [token.operate(*[stack.pop, stack.pop].reverse)]
227
+ elsif token.is_a?(Expression::Token::Immediate)
228
+ stack + [token.value]
229
+ elsif token.is_a?(Expression::Token::Variable)
230
+ eval_value = token_eval_proc.call(token)
231
+ unless eval_value.is_a?(Integer)
232
+ raise BadManipulationError, "Evaluation is faild. #{eval_value} is not Integer."
233
+ end
234
+ stack + [eval_value]
235
+ end
60
236
  end
237
+ raise BadManipulationError, "Cannot calculate this RPN." if stack.length != 1
238
+ return stack.last
61
239
  end
62
- raise ProgramAssertionError, "Cannot calc RPN." unless stack.length == 1
63
- return stack.last
64
240
  end
65
241
  end
66
242
  end
@@ -0,0 +1,20 @@
1
+ module BinaryParser
2
+ class FreeCondition
3
+ def initialize(&condition_proc)
4
+ @condition_proc = condition_proc
5
+ end
6
+
7
+ def eval(&name_eval_proc)
8
+ @name_eval_proc = name_eval_proc
9
+ return Proxy.new(self, []).instance_eval(&@condition_proc)
10
+ end
11
+
12
+ def symbol_call(symbol, *args, &block)
13
+ if symbol == :nextbits && args.length == 1
14
+ @name_eval_proc.call(Expression.nextbits_var(args[0]))
15
+ else
16
+ @name_eval_proc.call(Expression.value_var(symbol))
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,36 @@
1
+ module BinaryParser
2
+ module Memorize
3
+ module Extension
4
+
5
+ attr_accessor :memorize_methods
6
+
7
+ def method_added(method_name)
8
+ @memorized ||= Hash.new
9
+ if @memorize_methods.include?(method_name) && !@memorized[method_name]
10
+ @memorized[method_name] = true
11
+ memorize(method_name)
12
+ end
13
+ end
14
+
15
+ def memorize(method_name)
16
+ pure_method_name = "pure_#{method_name}".to_sym
17
+ alias_method pure_method_name, method_name
18
+ define_method(method_name) do |arg|
19
+ @memo ||= Hash.new
20
+ @memo[method_name] ||= Hash.new
21
+ @memo[method_name][arg] ||= send(pure_method_name, arg)
22
+ end
23
+ end
24
+ end
25
+
26
+ def self.one_arg_method(*method_names)
27
+ @method_names = method_names
28
+ return self
29
+ end
30
+
31
+ def self.included(klass)
32
+ klass.extend Extension
33
+ klass.memorize_methods = @method_names
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,14 @@
1
+ class Proxy < BasicObject
2
+ def initialize(target, proxy_methods)
3
+ @target = target
4
+ @proxy_methods = proxy_methods
5
+ end
6
+
7
+ def method_missing(message, *args, &block)
8
+ if @proxy_methods.include?(message)
9
+ @target.__send__(message, *args, &block)
10
+ else
11
+ @target.symbol_call(message, *args, &block)
12
+ end
13
+ end
14
+ end
data/lib/loop_list.rb CHANGED
@@ -31,6 +31,8 @@ module BinaryParser
31
31
  return @list.size
32
32
  end
33
33
 
34
+ alias_method :length, :size
35
+
34
36
  # String that describes this object.
35
37
  def content_description
36
38
  "list with #{size} elements"
data/lib/scope.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  module BinaryParser
2
2
  class Scope
3
+ include Memorize.one_arg_method(:load_var, :eval_bit_position, :eval_bit_length)
3
4
 
4
5
  attr_reader :abstract_binary
5
6
 
@@ -7,7 +8,6 @@ module BinaryParser
7
8
  @definition = structure_definition
8
9
  @abstract_binary = abstract_binary
9
10
  @parent_scope = parent_scope
10
- @data, @ebs, @ebl = {}, {}, {}
11
11
  end
12
12
 
13
13
  def names
@@ -18,16 +18,17 @@ module BinaryParser
18
18
  raise UndefinedError, "Undefined data-name '#{name}'." unless @definition[name]
19
19
  end
20
20
 
21
- # * Unsatisfactory memorized method (little obfuscated? : need refactoring?)
22
21
  def load_var(name)
23
22
  return @parent_scope.load_var(name) if !@definition[name] && @parent_scope
24
23
  check_name_defined(name)
25
24
  case @definition[name]
26
25
  when StructureDefinition::DataDefinition
27
- return @data[name] ||= eval_bit_length(name) == 0 ? nil :
28
- @definition[name].klass.new(load_binary(name))
26
+ eval_bit_length(name) == 0 ? nil : @definition[name].klass.new(load_binary(name))
29
27
  when StructureDefinition::LoopDefinition
30
- return @data[name] ||= LoopList.new(@definition[name], load_binary(name), self)
28
+ LoopList.new(@definition[name], load_binary(name), self)
29
+ when StructureDefinition::WhileDefinition
30
+ sub_binary = @abstract_binary.sub(:bit_index => eval_bit_position(name))
31
+ WhileList.new(@definition[name], sub_binary, self, name)
31
32
  else
32
33
  raise ProgramAssertionError, "Unknown definition-class '#{@definition[name].class}'."
33
34
  end
@@ -45,52 +46,61 @@ module BinaryParser
45
46
  end
46
47
  end
47
48
 
48
- # * memorized method (little obfuscated? : need refactoring?)
49
+ def preview_as_integer(start_pos, length)
50
+ sub_binary = @abstract_binary.sub(:bit_index => start_pos, :bit_length => length)
51
+ TemplateBase.new(sub_binary).to_i
52
+ end
53
+
49
54
  def eval_bit_position(name)
50
55
  check_name_defined(name)
51
- return @ebs[name] ||= @definition[name].bit_position.eval do |name|
52
- eval_bit_length(name)
53
- end
56
+ return eval(@definition[name].bit_position, nil)
54
57
  end
55
58
 
56
- # * memorized method (little obfuscated? : need refactoring?)
57
59
  def eval_bit_length(name)
58
60
  check_name_defined(name)
59
- return @ebl[name] if @ebl[name]
60
- return @ebl[name] = 0 unless @definition[name].conditions.all? do |cond|
61
- cond.eval{|name| load_var(name)}
61
+ unless @definition[name].conditions.all?{|cond| eval(cond, name)}
62
+ return 0
62
63
  end
63
- return @ebl[name] ||= @definition[name].bit_length.eval do |var_name|
64
- if var_name[0..1] == "__"
65
- bit_length_control_variable_resolution(name, var_name)
66
- else
67
- val = load_var(var_name)
68
- unless val
69
- raise ParsingError, "Variable '#{var_name}' assigned to Nil is referenced" +
70
- "at the time of resolving bit_length of '#{var_name}'."
71
- end
72
- val.to_i
73
- end
64
+ return eval(@definition[name].bit_length, name)
65
+ end
66
+
67
+ def eval_entire_bit_length
68
+ eval(@definition.bit_at, nil)
69
+ end
70
+
71
+ def eval(target, name)
72
+ target.eval do |token|
73
+ token_eval(token, name)
74
74
  end
75
75
  end
76
76
 
77
- def bit_length_control_variable_resolution(name, var_name)
78
- if var_name == :__rest
77
+ def token_eval(token, name)
78
+ if token.control_var?
79
+ bit_length_control_variable_resolution(name, token.symbol)
80
+ elsif token.nextbits_var?
81
+ preview_as_integer(eval_bit_position(name), token.length)
82
+ elsif token.length_var?
83
+ eval_bit_length(token.symbol)
84
+ elsif token.value_var?
85
+ unless val = load_var(token.symbol)
86
+ raise ParsingError, "Variable '#{token.symbol}' assigned to Nil is referenced" +
87
+ "at the time of resolving '#{name}'."
88
+ end
89
+ val.to_i
90
+ end
91
+ end
92
+
93
+ def bit_length_control_variable_resolution(name, symbol)
94
+ if symbol == :rest
79
95
  length = @abstract_binary.bit_length - eval_bit_position(name)
80
96
  raise ParsingError, "Binary is too short. (So, 'rest' is failed.)" if length < 0
81
97
  return length
82
- elsif var_name == :__position
98
+ elsif symbol == :position
83
99
  return eval_bit_position(name)
84
- elsif var_name[0..6] == "__LEN__"
85
- return eval_bit_length(var_name[7..(var_name.length - 1)].to_sym)
100
+ elsif symbol == :non_fixed
101
+ return load_var(name).bit_length
86
102
  else
87
- raise ProgramAssertionError, "Unknown Control-Variable '#{var_name}'."
88
- end
89
- end
90
-
91
- def eval_entire_bit_length
92
- return @definition.bit_at.eval do |name|
93
- eval_bit_length(name)
103
+ raise ProgramAssertionError, "Unknown Control-Variable '#{symbol}'."
94
104
  end
95
105
  end
96
106
  end