furnace-avm2 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (201) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +17 -0
  4. data/abcdump.abc +0 -0
  5. data/bin/furnace-avm2 +175 -0
  6. data/bin/furnace-avm2-benchmark +38 -0
  7. data/darkorbit.abc +0 -0
  8. data/furnace-avm2.gemspec +22 -0
  9. data/lib/avm2.rb +13 -0
  10. data/lib/avm2/abc.rb +65 -0
  11. data/lib/avm2/abc/metadata/const_pool_info.rb +18 -0
  12. data/lib/avm2/abc/metadata/exception_info.rb +29 -0
  13. data/lib/avm2/abc/metadata/file.rb +27 -0
  14. data/lib/avm2/abc/metadata/instance_info.rb +53 -0
  15. data/lib/avm2/abc/metadata/klass_info.rb +7 -0
  16. data/lib/avm2/abc/metadata/metadata_info.rb +9 -0
  17. data/lib/avm2/abc/metadata/method_body_info.rb +31 -0
  18. data/lib/avm2/abc/metadata/method_info.rb +57 -0
  19. data/lib/avm2/abc/metadata/multiname_info.rb +42 -0
  20. data/lib/avm2/abc/metadata/multiname_kind_genericname.rb +16 -0
  21. data/lib/avm2/abc/metadata/multiname_kind_multiname.rb +18 -0
  22. data/lib/avm2/abc/metadata/multiname_kind_multinamel.rb +17 -0
  23. data/lib/avm2/abc/metadata/multiname_kind_qname.rb +44 -0
  24. data/lib/avm2/abc/metadata/multiname_kind_rtqname.rb +17 -0
  25. data/lib/avm2/abc/metadata/multiname_kind_rtqnamel.rb +11 -0
  26. data/lib/avm2/abc/metadata/namespace_info.rb +22 -0
  27. data/lib/avm2/abc/metadata/ns_set_info.rb +13 -0
  28. data/lib/avm2/abc/metadata/option_detail.rb +26 -0
  29. data/lib/avm2/abc/metadata/option_info.rb +5 -0
  30. data/lib/avm2/abc/metadata/script_info.rb +7 -0
  31. data/lib/avm2/abc/metadata/trait_class.rb +9 -0
  32. data/lib/avm2/abc/metadata/trait_function.rb +9 -0
  33. data/lib/avm2/abc/metadata/trait_info.rb +55 -0
  34. data/lib/avm2/abc/metadata/trait_method.rb +12 -0
  35. data/lib/avm2/abc/metadata/trait_slot.rb +11 -0
  36. data/lib/avm2/abc/opcodes/arithmetic/as3_add.rb +8 -0
  37. data/lib/avm2/abc/opcodes/arithmetic/as3_add_i.rb +10 -0
  38. data/lib/avm2/abc/opcodes/arithmetic/as3_declocal.rb +14 -0
  39. data/lib/avm2/abc/opcodes/arithmetic/as3_declocal_i.rb +14 -0
  40. data/lib/avm2/abc/opcodes/arithmetic/as3_decrement.rb +10 -0
  41. data/lib/avm2/abc/opcodes/arithmetic/as3_decrement_i.rb +10 -0
  42. data/lib/avm2/abc/opcodes/arithmetic/as3_divide.rb +10 -0
  43. data/lib/avm2/abc/opcodes/arithmetic/as3_equals.rb +8 -0
  44. data/lib/avm2/abc/opcodes/arithmetic/as3_greaterequals.rb +8 -0
  45. data/lib/avm2/abc/opcodes/arithmetic/as3_greaterthan.rb +8 -0
  46. data/lib/avm2/abc/opcodes/arithmetic/as3_inclocal.rb +14 -0
  47. data/lib/avm2/abc/opcodes/arithmetic/as3_inclocal_i.rb +18 -0
  48. data/lib/avm2/abc/opcodes/arithmetic/as3_increment.rb +10 -0
  49. data/lib/avm2/abc/opcodes/arithmetic/as3_increment_i.rb +10 -0
  50. data/lib/avm2/abc/opcodes/arithmetic/as3_lessequals.rb +8 -0
  51. data/lib/avm2/abc/opcodes/arithmetic/as3_lessthan.rb +8 -0
  52. data/lib/avm2/abc/opcodes/arithmetic/as3_modulo.rb +10 -0
  53. data/lib/avm2/abc/opcodes/arithmetic/as3_multiply.rb +10 -0
  54. data/lib/avm2/abc/opcodes/arithmetic/as3_multiply_i.rb +10 -0
  55. data/lib/avm2/abc/opcodes/arithmetic/as3_negate.rb +10 -0
  56. data/lib/avm2/abc/opcodes/arithmetic/as3_negate_i.rb +10 -0
  57. data/lib/avm2/abc/opcodes/arithmetic/as3_not.rb +8 -0
  58. data/lib/avm2/abc/opcodes/arithmetic/as3_strictequals.rb +8 -0
  59. data/lib/avm2/abc/opcodes/arithmetic/as3_subtract.rb +10 -0
  60. data/lib/avm2/abc/opcodes/arithmetic/as3_subtract_i.rb +10 -0
  61. data/lib/avm2/abc/opcodes/arithmetic_opcode.rb +4 -0
  62. data/lib/avm2/abc/opcodes/bitwise/as3_bitand.rb +8 -0
  63. data/lib/avm2/abc/opcodes/bitwise/as3_bitnot.rb +8 -0
  64. data/lib/avm2/abc/opcodes/bitwise/as3_bitor.rb +8 -0
  65. data/lib/avm2/abc/opcodes/bitwise/as3_bitxor.rb +8 -0
  66. data/lib/avm2/abc/opcodes/bitwise/as3_lshift.rb +8 -0
  67. data/lib/avm2/abc/opcodes/bitwise/as3_rshift.rb +8 -0
  68. data/lib/avm2/abc/opcodes/bitwise/as3_urshift.rb +8 -0
  69. data/lib/avm2/abc/opcodes/bitwise_opcode.rb +4 -0
  70. data/lib/avm2/abc/opcodes/contextual_opcode.rb +32 -0
  71. data/lib/avm2/abc/opcodes/control_transfer/as3_ifeq.rb +14 -0
  72. data/lib/avm2/abc/opcodes/control_transfer/as3_iffalse.rb +14 -0
  73. data/lib/avm2/abc/opcodes/control_transfer/as3_ifge.rb +14 -0
  74. data/lib/avm2/abc/opcodes/control_transfer/as3_ifgt.rb +14 -0
  75. data/lib/avm2/abc/opcodes/control_transfer/as3_ifle.rb +14 -0
  76. data/lib/avm2/abc/opcodes/control_transfer/as3_iflt.rb +14 -0
  77. data/lib/avm2/abc/opcodes/control_transfer/as3_ifne.rb +14 -0
  78. data/lib/avm2/abc/opcodes/control_transfer/as3_ifnge.rb +14 -0
  79. data/lib/avm2/abc/opcodes/control_transfer/as3_ifngt.rb +14 -0
  80. data/lib/avm2/abc/opcodes/control_transfer/as3_ifnle.rb +14 -0
  81. data/lib/avm2/abc/opcodes/control_transfer/as3_ifnlt.rb +14 -0
  82. data/lib/avm2/abc/opcodes/control_transfer/as3_ifstricteq.rb +14 -0
  83. data/lib/avm2/abc/opcodes/control_transfer/as3_ifstrictne.rb +14 -0
  84. data/lib/avm2/abc/opcodes/control_transfer/as3_iftrue.rb +14 -0
  85. data/lib/avm2/abc/opcodes/control_transfer/as3_jump.rb +22 -0
  86. data/lib/avm2/abc/opcodes/control_transfer/as3_lookupswitch.rb +34 -0
  87. data/lib/avm2/abc/opcodes/control_transfer_opcode.rb +37 -0
  88. data/lib/avm2/abc/opcodes/exception/as3_newcatch.rb +16 -0
  89. data/lib/avm2/abc/opcodes/exception_opcode.rb +4 -0
  90. data/lib/avm2/abc/opcodes/function_invocation/as3_call.rb +16 -0
  91. data/lib/avm2/abc/opcodes/function_invocation/as3_callproperty.rb +7 -0
  92. data/lib/avm2/abc/opcodes/function_invocation/as3_callpropvoid.rb +7 -0
  93. data/lib/avm2/abc/opcodes/function_invocation/as3_callsuper.rb +7 -0
  94. data/lib/avm2/abc/opcodes/function_invocation/as3_callsupervoid.rb +7 -0
  95. data/lib/avm2/abc/opcodes/function_invocation_opcode.rb +17 -0
  96. data/lib/avm2/abc/opcodes/function_return/as3_returnvalue.rb +8 -0
  97. data/lib/avm2/abc/opcodes/function_return/as3_returnvoid.rb +8 -0
  98. data/lib/avm2/abc/opcodes/function_return_opcode.rb +4 -0
  99. data/lib/avm2/abc/opcodes/generic/as3_astypelate.rb +8 -0
  100. data/lib/avm2/abc/opcodes/generic/as3_checkfilter.rb +8 -0
  101. data/lib/avm2/abc/opcodes/generic/as3_dup.rb +8 -0
  102. data/lib/avm2/abc/opcodes/generic/as3_dxnslate.rb +8 -0
  103. data/lib/avm2/abc/opcodes/generic/as3_escxattr.rb +8 -0
  104. data/lib/avm2/abc/opcodes/generic/as3_getglobalscope.rb +8 -0
  105. data/lib/avm2/abc/opcodes/generic/as3_getlex.rb +16 -0
  106. data/lib/avm2/abc/opcodes/generic/as3_getscopeobject.rb +16 -0
  107. data/lib/avm2/abc/opcodes/generic/as3_getslot.rb +16 -0
  108. data/lib/avm2/abc/opcodes/generic/as3_hasnext.rb +8 -0
  109. data/lib/avm2/abc/opcodes/generic/as3_hasnext2.rb +17 -0
  110. data/lib/avm2/abc/opcodes/generic/as3_in.rb +8 -0
  111. data/lib/avm2/abc/opcodes/generic/as3_instanceof.rb +8 -0
  112. data/lib/avm2/abc/opcodes/generic/as3_istypelate.rb +8 -0
  113. data/lib/avm2/abc/opcodes/generic/as3_kill.rb +16 -0
  114. data/lib/avm2/abc/opcodes/generic/as3_label.rb +8 -0
  115. data/lib/avm2/abc/opcodes/generic/as3_nextname.rb +8 -0
  116. data/lib/avm2/abc/opcodes/generic/as3_nextvalue.rb +8 -0
  117. data/lib/avm2/abc/opcodes/generic/as3_nop.rb +8 -0
  118. data/lib/avm2/abc/opcodes/generic/as3_pop.rb +8 -0
  119. data/lib/avm2/abc/opcodes/generic/as3_popscope.rb +8 -0
  120. data/lib/avm2/abc/opcodes/generic/as3_setslot.rb +16 -0
  121. data/lib/avm2/abc/opcodes/generic/as3_swap.rb +8 -0
  122. data/lib/avm2/abc/opcodes/generic/as3_throw.rb +8 -0
  123. data/lib/avm2/abc/opcodes/generic/as3_typeof.rb +8 -0
  124. data/lib/avm2/abc/opcodes/load_store/as3_getlocal.rb +15 -0
  125. data/lib/avm2/abc/opcodes/load_store/as3_getlocal_0.rb +11 -0
  126. data/lib/avm2/abc/opcodes/load_store/as3_getlocal_1.rb +11 -0
  127. data/lib/avm2/abc/opcodes/load_store/as3_getlocal_2.rb +11 -0
  128. data/lib/avm2/abc/opcodes/load_store/as3_getlocal_3.rb +11 -0
  129. data/lib/avm2/abc/opcodes/load_store/as3_setlocal.rb +15 -0
  130. data/lib/avm2/abc/opcodes/load_store/as3_setlocal_0.rb +11 -0
  131. data/lib/avm2/abc/opcodes/load_store/as3_setlocal_1.rb +11 -0
  132. data/lib/avm2/abc/opcodes/load_store/as3_setlocal_2.rb +11 -0
  133. data/lib/avm2/abc/opcodes/load_store/as3_setlocal_3.rb +11 -0
  134. data/lib/avm2/abc/opcodes/load_store_opcode.rb +18 -0
  135. data/lib/avm2/abc/opcodes/object_manipulation/as3_construct.rb +12 -0
  136. data/lib/avm2/abc/opcodes/object_manipulation/as3_constructsuper.rb +16 -0
  137. data/lib/avm2/abc/opcodes/object_manipulation/as3_newactivation.rb +8 -0
  138. data/lib/avm2/abc/opcodes/object_manipulation/as3_newarray.rb +12 -0
  139. data/lib/avm2/abc/opcodes/object_manipulation/as3_newclass.rb +12 -0
  140. data/lib/avm2/abc/opcodes/object_manipulation/as3_newfunction.rb +12 -0
  141. data/lib/avm2/abc/opcodes/object_manipulation/as3_newobject.rb +12 -0
  142. data/lib/avm2/abc/opcodes/object_manipulation_opcode.rb +4 -0
  143. data/lib/avm2/abc/opcodes/opcode.rb +127 -0
  144. data/lib/avm2/abc/opcodes/property/as3_constructprop.rb +20 -0
  145. data/lib/avm2/abc/opcodes/property/as3_deleteproperty.rb +9 -0
  146. data/lib/avm2/abc/opcodes/property/as3_findproperty.rb +9 -0
  147. data/lib/avm2/abc/opcodes/property/as3_findpropstrict.rb +9 -0
  148. data/lib/avm2/abc/opcodes/property/as3_getdescendants.rb +9 -0
  149. data/lib/avm2/abc/opcodes/property/as3_getproperty.rb +9 -0
  150. data/lib/avm2/abc/opcodes/property/as3_getsuper.rb +9 -0
  151. data/lib/avm2/abc/opcodes/property/as3_initproperty.rb +9 -0
  152. data/lib/avm2/abc/opcodes/property/as3_setproperty.rb +9 -0
  153. data/lib/avm2/abc/opcodes/property/as3_setsuper.rb +9 -0
  154. data/lib/avm2/abc/opcodes/property_opcode.rb +13 -0
  155. data/lib/avm2/abc/opcodes/push_literal/as3_pushbyte.rb +11 -0
  156. data/lib/avm2/abc/opcodes/push_literal/as3_pushdouble.rb +11 -0
  157. data/lib/avm2/abc/opcodes/push_literal/as3_pushfalse.rb +7 -0
  158. data/lib/avm2/abc/opcodes/push_literal/as3_pushint.rb +11 -0
  159. data/lib/avm2/abc/opcodes/push_literal/as3_pushnan.rb +7 -0
  160. data/lib/avm2/abc/opcodes/push_literal/as3_pushnull.rb +7 -0
  161. data/lib/avm2/abc/opcodes/push_literal/as3_pushscope.rb +8 -0
  162. data/lib/avm2/abc/opcodes/push_literal/as3_pushshort.rb +11 -0
  163. data/lib/avm2/abc/opcodes/push_literal/as3_pushstring.rb +11 -0
  164. data/lib/avm2/abc/opcodes/push_literal/as3_pushtrue.rb +7 -0
  165. data/lib/avm2/abc/opcodes/push_literal/as3_pushundefined.rb +7 -0
  166. data/lib/avm2/abc/opcodes/push_literal/as3_pushwith.rb +8 -0
  167. data/lib/avm2/abc/opcodes/push_literal_opcode.rb +18 -0
  168. data/lib/avm2/abc/opcodes/type_conversion/as3_applytype.rb +12 -0
  169. data/lib/avm2/abc/opcodes/type_conversion/as3_coerce.rb +16 -0
  170. data/lib/avm2/abc/opcodes/type_conversion/as3_coerce_a.rb +8 -0
  171. data/lib/avm2/abc/opcodes/type_conversion/as3_coerce_b.rb +10 -0
  172. data/lib/avm2/abc/opcodes/type_conversion/as3_coerce_s.rb +10 -0
  173. data/lib/avm2/abc/opcodes/type_conversion/as3_convert_d.rb +10 -0
  174. data/lib/avm2/abc/opcodes/type_conversion/as3_convert_i.rb +10 -0
  175. data/lib/avm2/abc/opcodes/type_conversion/as3_convert_o.rb +10 -0
  176. data/lib/avm2/abc/opcodes/type_conversion/as3_convert_s.rb +10 -0
  177. data/lib/avm2/abc/opcodes/type_conversion/as3_convert_u.rb +10 -0
  178. data/lib/avm2/abc/opcodes/type_conversion_opcode.rb +4 -0
  179. data/lib/avm2/abc/primitives/opcode_sequence.rb +195 -0
  180. data/lib/avm2/abc/primitives/record.rb +132 -0
  181. data/lib/avm2/binary/choice_definition.rb +21 -0
  182. data/lib/avm2/binary/record.rb +469 -0
  183. data/lib/avm2/transform.rb +6 -0
  184. data/lib/avm2/transform/ast_build.rb +206 -0
  185. data/lib/avm2/transform/ast_normalize.rb +0 -0
  186. data/lib/avm2/version.rb +3 -0
  187. data/test/exception.abc +0 -0
  188. data/test/exception.as +29 -0
  189. data/test/literal.abc +0 -0
  190. data/test/literal.as +5 -0
  191. data/test/logic.abc +0 -0
  192. data/test/logic.as +23 -0
  193. data/test/loops.abc +0 -0
  194. data/test/loops.as +30 -0
  195. data/test/number.abc +0 -0
  196. data/test/number.as +14 -0
  197. data/test/switch.abc +0 -0
  198. data/test/switch.as +38 -0
  199. data/test/ternary.abc +0 -0
  200. data/test/ternary.as +22 -0
  201. metadata +579 -0
@@ -0,0 +1,21 @@
1
+ module AVM2::Binary
2
+ class ChoiceDefinition
3
+ def initialize
4
+ @data = []
5
+ end
6
+
7
+ def self.parse(block)
8
+ instance = new
9
+ instance.instance_exec(&block)
10
+ instance.to_a
11
+ end
12
+
13
+ def variant(value, type, sub_options={})
14
+ @data << [ value, type, sub_options ]
15
+ end
16
+
17
+ def to_a
18
+ @data
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,469 @@
1
+ module AVM2::Binary
2
+ class Record
3
+ class << self
4
+ attr_reader :format, :codebase
5
+
6
+ def inherited(klass)
7
+ AVM2::Binary::Record.register klass
8
+
9
+ klass.class_exec do
10
+ @format = []
11
+ end
12
+ end
13
+
14
+ def method_missing(method, name, options={}, &block)
15
+ if instance_methods.include? :"read_#{method}"
16
+ options.merge!(:block => block) if block
17
+
18
+ check(method, options)
19
+
20
+ attr_accessor name
21
+ @format << [method, name, options.freeze]
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ def register(klass)
28
+ @codebase ||= []
29
+ @codebase << klass
30
+ end
31
+
32
+ def codegen_each
33
+ AVM2::Binary::Record.codebase.each do |klass|
34
+ klass.codegen
35
+ end
36
+ end
37
+
38
+ def codegen
39
+ tracing = $avm2_binary_trace
40
+
41
+ gen_if = lambda do |code, options, &block|
42
+ if options.include? :if
43
+ if options[:if].to_proc.arity == 0
44
+ code << "if instance_exec(&options[:if])"
45
+ else
46
+ code << "if instance_exec(self, &options[:if])"
47
+ end
48
+ end
49
+
50
+ block.call(code)
51
+
52
+ if options.include? :if
53
+ code << "end"
54
+ end
55
+ end
56
+
57
+ gen_read = lambda do |index|
58
+ method, name, options = @format[index]
59
+ code = []
60
+
61
+ code << "options = self.class.format[#{index}][2]"
62
+
63
+ gen_if.(code, options) do
64
+ code << "self.class.trace_scope(#{name}) do" if tracing
65
+ code << " value = read_#{method}(io, options)"
66
+ code << " self.class.trace_value(value)" if tracing
67
+ code << " @#{name} = value"
68
+ code << "end" if tracing
69
+ end
70
+
71
+ code.join "\n"
72
+ end
73
+
74
+ gen_write = lambda do |index|
75
+ method, name, options = @format[index]
76
+ code = []
77
+
78
+ code << "options = self.class.format[#{index}][2]"
79
+
80
+ gen_if.(code, options) do
81
+ code << "self.class.trace_scope(#{name}) do" if tracing
82
+ if options.include? :value
83
+ code << "value = fetch(options[:value])"
84
+ else
85
+ code << "value = @#{name}"
86
+ end
87
+ code << " self.class.trace_value(value)" if tracing
88
+ code << " value = write_#{method}(io, value, options)"
89
+ code << "end" if tracing
90
+ end
91
+
92
+ code.join "\n"
93
+ end
94
+
95
+ class_eval <<-CODE, "(generated-io:#{self})"
96
+ def initialize(options={})
97
+ @root = options[:parent].root if options[:parent]
98
+ #{@format.map { |f| "@#{f[1]} = nil\n" }.join}
99
+
100
+ #{"initialize_record(options)" if instance_methods.include? :initialize_record}
101
+ end
102
+
103
+ def read(io)
104
+ #{"before_read(io)" if instance_methods.include? :before_read}
105
+ #{@format.each_index.map { |index| gen_read.(index) }.join "\n"}
106
+ #{"after_read(io)" if instance_methods.include? :after_read}
107
+ end
108
+
109
+ def write(io)
110
+ #{"before_write(io)" if instance_methods.include? :before_write}
111
+ #{@format.each_index.map { |index| gen_write.(index) }.join "\n"}
112
+ #{"after_write(io)" if instance_methods.include? :after_write}
113
+ end
114
+ CODE
115
+ end
116
+
117
+ attr_accessor :tracing
118
+
119
+ def trace
120
+ AVM2::Binary::Record.tracing = true
121
+
122
+ yield
123
+ ensure
124
+ AVM2::Binary::Record.tracing = false
125
+ end
126
+
127
+ def trace_scope(scope)
128
+ Thread.current[:binary_trace_scope] ||= []
129
+ Thread.current[:binary_trace_scope].push scope
130
+ Thread.current[:binary_trace_nested] = false
131
+
132
+ yield
133
+ ensure
134
+ Thread.current[:binary_trace_scope].pop
135
+ Thread.current[:binary_trace_nested] = true
136
+ end
137
+
138
+ def trace_value(value)
139
+ return if value.is_a?(Array) && value[0].is_a?(Record)
140
+
141
+ puts "#{Thread.current[:binary_trace_scope].join(".")} = #{value}"
142
+ end
143
+ end
144
+
145
+ attr_reader :root
146
+
147
+ def to_hash
148
+ hash = {}
149
+
150
+ self.class.format.each do |method, name, options|
151
+ hash[name] = instance_variable_get :"@#{name}"
152
+ end
153
+
154
+ hash
155
+ end
156
+
157
+ def inspect
158
+ to_hash.inspect
159
+ end
160
+
161
+ def byte_length
162
+ self.class.format.map do |method, name, options|
163
+ send(:"length_#{method}", instance_variable_get(:"@#{name}"), options)
164
+ end.reduce(0, :+)
165
+ end
166
+
167
+ protected
168
+
169
+ # uint8
170
+
171
+ def read_uint8(io, options)
172
+ io.read(1).unpack("C").at(0)
173
+ end
174
+
175
+ def write_uint8(io, value, options)
176
+ io.write([value].pack("C"))
177
+ end
178
+
179
+ def length_uint8(value, options)
180
+ 1
181
+ end
182
+
183
+ # uint16
184
+
185
+ def read_uint16(io, options)
186
+ io.read(2).unpack("v").at(0)
187
+ end
188
+
189
+ def write_uint16(io, value, options)
190
+ io.write([value].pack("v"))
191
+ end
192
+
193
+ def length_uint16(value, options)
194
+ 2
195
+ end
196
+
197
+ # int24
198
+
199
+ def read_int24(io, options)
200
+ lo, hi = io.read(3).unpack("vC")
201
+ value = lo | (hi << 16)
202
+ if value & 0x800000 != 0
203
+ -(-value & 0x7fffff)
204
+ else
205
+ value
206
+ end
207
+ end
208
+
209
+ def write_int24(io, value, options)
210
+ lo, hi = value & 0xffff, (value >> 16) & 0xff
211
+ io.write([lo, hi].pack("vC"))
212
+ end
213
+
214
+ def length_int24(value, options)
215
+ 3
216
+ end
217
+
218
+ # double
219
+
220
+ def read_double(io, options)
221
+ io.read(8).unpack("E").at(0)
222
+ end
223
+
224
+ def write_double(io, value, options)
225
+ io.write([value].pack("E"))
226
+ end
227
+
228
+ def length_double(value, options)
229
+ 8
230
+ end
231
+
232
+ # vint32
233
+
234
+ def read_vint32(io, options)
235
+ read_vint(io, true)
236
+ end
237
+
238
+ def write_vint32(io, value, options)
239
+ write_vint(io, value, true)
240
+ end
241
+
242
+ def length_vint32(value, options)
243
+ length_vint(value)
244
+ end
245
+
246
+ # vuint32
247
+
248
+ def read_vuint32(io, options)
249
+ read_vint(io, false)
250
+ end
251
+
252
+ def write_vuint32(io, value, options)
253
+ write_vint(io, value, true)
254
+ end
255
+
256
+ def length_vuint32(value, options)
257
+ length_vint(value)
258
+ end
259
+
260
+ # vuint30
261
+
262
+ def read_vuint30(io, options)
263
+ read_vint(io, false)
264
+ end
265
+
266
+ def write_vuint30(io, value, options)
267
+ write_vint(io, value, true)
268
+ end
269
+
270
+ def length_vuint30(value, options)
271
+ length_vint(value)
272
+ end
273
+
274
+ # vstring
275
+
276
+ def read_vstring(io, options)
277
+ length = read_vuint32(io, {})
278
+ io.read(length)
279
+ end
280
+
281
+ def write_vstring(io, value, options)
282
+ write_vuint32(io, value.bytesize, {})
283
+ io.write(value)
284
+ end
285
+
286
+ # nested
287
+
288
+ def self.check_nested(options={})
289
+ do_check(options, "nested", [:class])
290
+ end
291
+
292
+ def read_nested(io, options)
293
+ nested = options[:class].new((options[:options] || {}).merge(:parent => self))
294
+ nested.read(io)
295
+ nested
296
+ end
297
+
298
+ def write_nested(io, value, options)
299
+ if value.class != options[:class]
300
+ raise Exception, "conflicting types in nested write: #{options[:class]} != #{value.class}"
301
+ end
302
+
303
+ value.write(io)
304
+ end
305
+
306
+ # array
307
+
308
+ def self.check_array(options={})
309
+ do_check(options, "array", [:initial_length, :type], [:options])
310
+
311
+ check(options[:type], options[:options] || {})
312
+ end
313
+
314
+ def read_array(io, options)
315
+ length = fetch(options[:initial_length])
316
+
317
+ array = []
318
+ length.times do |index|
319
+ #self.class.trace_scope(index.to_s) do
320
+ array << send(:"read_#{options[:type]}", io, options[:options])
321
+ #end
322
+ end
323
+ array
324
+ end
325
+
326
+ def write_array(io, value, options)
327
+ value.each do |element|
328
+ send(:"write_#{options[:type]}", io, element, options[:options])
329
+ end
330
+ end
331
+
332
+ def length_array(value, options)
333
+ value.map do |element|
334
+ send(:"length_#{options[:type]}", element, options[:options])
335
+ end.reduce(0, :+)
336
+ end
337
+
338
+ # choice
339
+
340
+ def self.check_choice(options={})
341
+ do_check(options, "choice", [:selection, :block])
342
+
343
+ options[:choice] = ChoiceDefinition.parse(options.delete(:block))
344
+ end
345
+
346
+ def read_choice(io, options)
347
+ selection = fetch(options[:selection])
348
+
349
+ options[:choice].each do |variant, type, sub_options|
350
+ if selection == variant
351
+ return send(:"read_#{type}", io, sub_options)
352
+ end
353
+ end
354
+
355
+ raise Exception, "unknown choice value #{value}"
356
+ end
357
+
358
+ def write_choice(io, value, options)
359
+ selection = fetch(options[:selection])
360
+
361
+ options[:choice].each do |variant, type, sub_options|
362
+ if selection == variant
363
+ return send(:"write_#{type}", io, value, sub_options)
364
+ end
365
+ end
366
+
367
+ raise Exception, "unknown choice value #{value}"
368
+ end
369
+
370
+ def length_choice(value)
371
+ value.length
372
+ end
373
+
374
+ # Common
375
+
376
+ def fetch(what)
377
+ if what.respond_to? :call
378
+ instance_exec &what
379
+ elsif what.is_a? Symbol
380
+ send what
381
+ else
382
+ raise Exception, "cannot fetch #{what.inspect}"
383
+ end
384
+ end
385
+
386
+ def self.check(type, options)
387
+ if respond_to? :"check_#{type}"
388
+ send :"check_#{type}", options
389
+ end
390
+ end
391
+
392
+ def self.do_check(original_options, name, mandatory=[], optional=[])
393
+ options = original_options.clone
394
+
395
+ mandatory.each do |arg|
396
+ raise Exception, "#{name} records require #{arg.inspect} to be set" unless options.delete(arg)
397
+ end
398
+
399
+ optional.each do |arg|
400
+ options.delete(arg)
401
+ end
402
+
403
+ # Global options
404
+ options.delete(:if)
405
+
406
+ if options.any?
407
+ raise Exception, "extra options #{options.keys.inspect} for #{name} record"
408
+ end
409
+ end
410
+
411
+ # Generics
412
+
413
+ def read_vint(io, signed)
414
+ value = 0
415
+ bit_shift = 0
416
+
417
+ begin
418
+ byte = io.read(1).unpack("C").at(0)
419
+
420
+ value |= (byte & 0x7F) << bit_shift
421
+ bit_shift += 7
422
+ end while byte & 0x80 != 0
423
+
424
+ sign_bit = (1 << (bit_shift - 1))
425
+ if signed && (value & sign_bit) != 0
426
+ value = -(value & ~sign_bit)
427
+ end
428
+
429
+ value
430
+ end
431
+
432
+ def write_vint(io, value, signed)
433
+ if value < 0 && signed
434
+ value = -value
435
+ is_negative = true
436
+ else
437
+ is_negative = false
438
+ end
439
+
440
+ bytes = []
441
+
442
+ begin
443
+ byte = value & 0x7F
444
+ value >>= 7
445
+
446
+ if value != 0
447
+ byte |= 0x80
448
+ elsif is_negative
449
+ byte |= 0x40
450
+ end
451
+
452
+ bytes.push(byte)
453
+ end while value != 0
454
+
455
+ io.write bytes.pack("C*")
456
+ end
457
+
458
+ def length_vint(value)
459
+ length = 0
460
+
461
+ begin
462
+ length += 1
463
+ value >>= 7
464
+ end while value != 0
465
+
466
+ length
467
+ end
468
+ end
469
+ end