binary_parser 1.1.1 → 1.1.2

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.
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