scalm-RocketAMF 1.0.0

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.
Files changed (106) hide show
  1. data/README.rdoc +47 -0
  2. data/Rakefile +59 -0
  3. data/RocketAMF.gemspec +20 -0
  4. data/benchmark.rb +74 -0
  5. data/ext/rocketamf_ext/class_mapping.c +484 -0
  6. data/ext/rocketamf_ext/constants.h +52 -0
  7. data/ext/rocketamf_ext/deserializer.c +776 -0
  8. data/ext/rocketamf_ext/deserializer.h +28 -0
  9. data/ext/rocketamf_ext/extconf.rb +18 -0
  10. data/ext/rocketamf_ext/remoting.c +184 -0
  11. data/ext/rocketamf_ext/rocketamf_ext.c +38 -0
  12. data/ext/rocketamf_ext/serializer.c +834 -0
  13. data/ext/rocketamf_ext/serializer.h +29 -0
  14. data/ext/rocketamf_ext/utility.h +4 -0
  15. data/lib/rocketamf.rb +216 -0
  16. data/lib/rocketamf/class_mapping.rb +237 -0
  17. data/lib/rocketamf/constants.rb +50 -0
  18. data/lib/rocketamf/ext.rb +28 -0
  19. data/lib/rocketamf/extensions.rb +22 -0
  20. data/lib/rocketamf/pure.rb +24 -0
  21. data/lib/rocketamf/pure/deserializer.rb +455 -0
  22. data/lib/rocketamf/pure/io_helpers.rb +94 -0
  23. data/lib/rocketamf/pure/remoting.rb +117 -0
  24. data/lib/rocketamf/pure/serializer.rb +474 -0
  25. data/lib/rocketamf/remoting.rb +196 -0
  26. data/lib/rocketamf/values/messages.rb +214 -0
  27. data/lib/rocketamf/values/typed_hash.rb +13 -0
  28. data/spec/class_mapping_spec.rb +110 -0
  29. data/spec/deserializer_spec.rb +455 -0
  30. data/spec/fast_class_mapping_spec.rb +144 -0
  31. data/spec/fixtures/objects/amf0-boolean.bin +1 -0
  32. data/spec/fixtures/objects/amf0-complex-encoded-string.bin +0 -0
  33. data/spec/fixtures/objects/amf0-date.bin +0 -0
  34. data/spec/fixtures/objects/amf0-ecma-ordinal-array.bin +0 -0
  35. data/spec/fixtures/objects/amf0-empty-string-key-hash.bin +0 -0
  36. data/spec/fixtures/objects/amf0-hash.bin +0 -0
  37. data/spec/fixtures/objects/amf0-null.bin +1 -0
  38. data/spec/fixtures/objects/amf0-number.bin +0 -0
  39. data/spec/fixtures/objects/amf0-object.bin +0 -0
  40. data/spec/fixtures/objects/amf0-ref-test.bin +0 -0
  41. data/spec/fixtures/objects/amf0-strict-array.bin +0 -0
  42. data/spec/fixtures/objects/amf0-string.bin +0 -0
  43. data/spec/fixtures/objects/amf0-time.bin +0 -0
  44. data/spec/fixtures/objects/amf0-typed-object.bin +0 -0
  45. data/spec/fixtures/objects/amf0-undefined.bin +1 -0
  46. data/spec/fixtures/objects/amf0-untyped-object.bin +0 -0
  47. data/spec/fixtures/objects/amf0-xml-doc.bin +0 -0
  48. data/spec/fixtures/objects/amf3-0.bin +0 -0
  49. data/spec/fixtures/objects/amf3-array-collection.bin +2 -0
  50. data/spec/fixtures/objects/amf3-array-ref.bin +1 -0
  51. data/spec/fixtures/objects/amf3-associative-array.bin +1 -0
  52. data/spec/fixtures/objects/amf3-bigNum.bin +0 -0
  53. data/spec/fixtures/objects/amf3-byte-array-ref.bin +1 -0
  54. data/spec/fixtures/objects/amf3-byte-array.bin +0 -0
  55. data/spec/fixtures/objects/amf3-complex-array-collection.bin +6 -0
  56. data/spec/fixtures/objects/amf3-complex-encoded-string-array.bin +1 -0
  57. data/spec/fixtures/objects/amf3-date-ref.bin +0 -0
  58. data/spec/fixtures/objects/amf3-date.bin +0 -0
  59. data/spec/fixtures/objects/amf3-dictionary.bin +0 -0
  60. data/spec/fixtures/objects/amf3-dynamic-object.bin +2 -0
  61. data/spec/fixtures/objects/amf3-empty-array-ref.bin +1 -0
  62. data/spec/fixtures/objects/amf3-empty-array.bin +1 -0
  63. data/spec/fixtures/objects/amf3-empty-dictionary.bin +0 -0
  64. data/spec/fixtures/objects/amf3-empty-string-ref.bin +1 -0
  65. data/spec/fixtures/objects/amf3-encoded-string-ref.bin +0 -0
  66. data/spec/fixtures/objects/amf3-externalizable.bin +0 -0
  67. data/spec/fixtures/objects/amf3-false.bin +1 -0
  68. data/spec/fixtures/objects/amf3-float.bin +0 -0
  69. data/spec/fixtures/objects/amf3-graph-member.bin +0 -0
  70. data/spec/fixtures/objects/amf3-hash.bin +2 -0
  71. data/spec/fixtures/objects/amf3-large-max.bin +0 -0
  72. data/spec/fixtures/objects/amf3-large-min.bin +0 -0
  73. data/spec/fixtures/objects/amf3-max.bin +1 -0
  74. data/spec/fixtures/objects/amf3-min.bin +0 -0
  75. data/spec/fixtures/objects/amf3-mixed-array.bin +10 -0
  76. data/spec/fixtures/objects/amf3-null.bin +1 -0
  77. data/spec/fixtures/objects/amf3-object-ref.bin +0 -0
  78. data/spec/fixtures/objects/amf3-primitive-array.bin +1 -0
  79. data/spec/fixtures/objects/amf3-string-ref.bin +0 -0
  80. data/spec/fixtures/objects/amf3-string.bin +1 -0
  81. data/spec/fixtures/objects/amf3-symbol.bin +1 -0
  82. data/spec/fixtures/objects/amf3-trait-ref.bin +3 -0
  83. data/spec/fixtures/objects/amf3-true.bin +1 -0
  84. data/spec/fixtures/objects/amf3-typed-object.bin +2 -0
  85. data/spec/fixtures/objects/amf3-vector-double.bin +0 -0
  86. data/spec/fixtures/objects/amf3-vector-int.bin +0 -0
  87. data/spec/fixtures/objects/amf3-vector-object.bin +0 -0
  88. data/spec/fixtures/objects/amf3-vector-uint.bin +0 -0
  89. data/spec/fixtures/objects/amf3-xml-doc.bin +1 -0
  90. data/spec/fixtures/objects/amf3-xml-ref.bin +1 -0
  91. data/spec/fixtures/objects/amf3-xml.bin +1 -0
  92. data/spec/fixtures/request/acknowledge-response.bin +0 -0
  93. data/spec/fixtures/request/amf0-error-response.bin +0 -0
  94. data/spec/fixtures/request/blaze-response.bin +0 -0
  95. data/spec/fixtures/request/commandMessage.bin +0 -0
  96. data/spec/fixtures/request/flex-request.bin +0 -0
  97. data/spec/fixtures/request/multiple-simple-request.bin +0 -0
  98. data/spec/fixtures/request/remotingMessage.bin +0 -0
  99. data/spec/fixtures/request/simple-request.bin +0 -0
  100. data/spec/fixtures/request/simple-response.bin +0 -0
  101. data/spec/fixtures/request/unsupportedCommandMessage.bin +0 -0
  102. data/spec/messages_spec.rb +39 -0
  103. data/spec/remoting_spec.rb +196 -0
  104. data/spec/serializer_spec.rb +503 -0
  105. data/spec/spec_helper.rb +55 -0
  106. metadata +164 -0
@@ -0,0 +1,50 @@
1
+ module RocketAMF
2
+ # AMF0 Type Markers
3
+ AMF0_NUMBER_MARKER = 0x00 #"\000"
4
+ AMF0_BOOLEAN_MARKER = 0x01 #"\001"
5
+ AMF0_STRING_MARKER = 0x02 #"\002"
6
+ AMF0_OBJECT_MARKER = 0x03 #"\003"
7
+ AMF0_MOVIE_CLIP_MARKER = 0x04 #"\004" # Unused
8
+ AMF0_NULL_MARKER = 0x05 #"\005"
9
+ AMF0_UNDEFINED_MARKER = 0x06 #"\006"
10
+ AMF0_REFERENCE_MARKER = 0x07 #"\a"
11
+ AMF0_HASH_MARKER = 0x08 #"\b"
12
+ AMF0_OBJECT_END_MARKER = 0x09 #"\t"
13
+ AMF0_STRICT_ARRAY_MARKER = 0x0A #"\n"
14
+ AMF0_DATE_MARKER = 0x0B #"\v"
15
+ AMF0_LONG_STRING_MARKER = 0x0C #"\f"
16
+ AMF0_UNSUPPORTED_MARKER = 0x0D #"\r"
17
+ AMF0_RECORDSET_MARKER = 0x0E #"\016" # Unused
18
+ AMF0_XML_MARKER = 0x0F #"\017"
19
+ AMF0_TYPED_OBJECT_MARKER = 0x10 #"\020"
20
+ AMF0_AMF3_MARKER = 0x11 #"\021"
21
+
22
+ # AMF3 Type Markers
23
+ AMF3_UNDEFINED_MARKER = 0x00 #"\000"
24
+ AMF3_NULL_MARKER = 0x01 #"\001"
25
+ AMF3_FALSE_MARKER = 0x02 #"\002"
26
+ AMF3_TRUE_MARKER = 0x03 #"\003"
27
+ AMF3_INTEGER_MARKER = 0x04 #"\004"
28
+ AMF3_DOUBLE_MARKER = 0x05 #"\005"
29
+ AMF3_STRING_MARKER = 0x06 #"\006"
30
+ AMF3_XML_DOC_MARKER = 0x07 #"\a"
31
+ AMF3_DATE_MARKER = 0x08 #"\b"
32
+ AMF3_ARRAY_MARKER = 0x09 #"\t"
33
+ AMF3_OBJECT_MARKER = 0x0A #"\n"
34
+ AMF3_XML_MARKER = 0x0B #"\v"
35
+ AMF3_BYTE_ARRAY_MARKER = 0x0C #"\f"
36
+ AMF3_VECTOR_INT_MARKER = 0x0D #"\r"
37
+ AMF3_VECTOR_UINT_MARKER = 0x0E #"\016"
38
+ AMF3_VECTOR_DOUBLE_MARKER = 0x0F #"\017"
39
+ AMF3_VECTOR_OBJECT_MARKER = 0x10 #"\020"
40
+ AMF3_DICT_MARKER = 0x11 #"\021"
41
+
42
+ # Other AMF3 Markers
43
+ AMF3_EMPTY_STRING = 0x01
44
+ AMF3_CLOSE_DYNAMIC_OBJECT = 0x01
45
+ AMF3_CLOSE_DYNAMIC_ARRAY = 0x01
46
+
47
+ # Other Constants
48
+ MAX_INTEGER = 268435455
49
+ MIN_INTEGER = -268435456
50
+ end
@@ -0,0 +1,28 @@
1
+ begin
2
+ # Fat binaries for Windows
3
+ RUBY_VERSION =~ /(\d+.\d+)/
4
+ require "#{$1}/rocketamf_ext"
5
+ rescue LoadError
6
+ require "rocketamf_ext"
7
+ end
8
+
9
+ module RocketAMF
10
+ # This module holds all the modules/classes that implement AMF's functionality
11
+ # in C
12
+ module Ext
13
+ $DEBUG and warn "Using C library for RocketAMF."
14
+ end
15
+
16
+ #:stopdoc:
17
+ # Import serializer/deserializer
18
+ Deserializer = RocketAMF::Ext::Deserializer
19
+ Serializer = RocketAMF::Ext::Serializer
20
+
21
+ # Modify envelope so it can serialize/deserialize
22
+ class Envelope
23
+ remove_method :populate_from_stream
24
+ remove_method :serialize
25
+ include RocketAMF::Ext::Envelope
26
+ end
27
+ #:startdoc:
28
+ end
@@ -0,0 +1,22 @@
1
+ # Joc's monkeypatch for string bytesize (only available in 1.8.7+)
2
+ if !"amf".respond_to? :bytesize
3
+ class String #:nodoc:
4
+ def bytesize
5
+ self.size
6
+ end
7
+ end
8
+ end
9
+
10
+ # Add <tt>ArrayCollection</tt> override to arrays
11
+ class Array
12
+ # Override <tt>RocketAMF::ClassMapper.use_array_collection</tt> setting for
13
+ # this array. Adds <tt>is_array_collection?</tt> method, which is used by the
14
+ # serializer over the global config if defined.
15
+ def is_array_collection= a
16
+ @is_array_collection = a
17
+
18
+ def self.is_array_collection? #:nodoc:
19
+ @is_array_collection
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ require 'rocketamf/pure/deserializer'
2
+ require 'rocketamf/pure/serializer'
3
+ require 'rocketamf/pure/remoting'
4
+
5
+ module RocketAMF
6
+ # This module holds all the modules/classes that implement AMF's functionality
7
+ # in pure ruby
8
+ module Pure
9
+ $DEBUG and warn "Using pure library for RocketAMF."
10
+ end
11
+
12
+ #:stopdoc:
13
+ # Import serializer/deserializer
14
+ Deserializer = RocketAMF::Pure::Deserializer
15
+ Serializer = RocketAMF::Pure::Serializer
16
+
17
+ # Modify envelope so it can serialize/deserialize
18
+ class Envelope
19
+ remove_method :populate_from_stream
20
+ remove_method :serialize
21
+ include RocketAMF::Pure::Envelope
22
+ end
23
+ #:startdoc:
24
+ end
@@ -0,0 +1,455 @@
1
+ require 'rocketamf/pure/io_helpers'
2
+
3
+ module RocketAMF
4
+ module Pure
5
+ # Pure ruby deserializer for AMF0 and AMF3
6
+ class Deserializer
7
+ attr_accessor :source
8
+
9
+ # Pass in the class mapper instance to use when deserializing. This
10
+ # enables better caching behavior in the class mapper and allows
11
+ # one to change mappings between deserialization attempts.
12
+ def initialize class_mapper
13
+ @class_mapper = class_mapper
14
+ end
15
+
16
+ # Deserialize the source using AMF0 or AMF3. Source should either
17
+ # be a string or StringIO object. If you pass a StringIO object,
18
+ # it will have its position updated to the end of the deserialized
19
+ # data.
20
+ def deserialize version, source
21
+ raise ArgumentError, "unsupported version #{version}" unless [0,3].include?(version)
22
+ @version = version
23
+
24
+ if StringIO === source
25
+ @source = source
26
+ elsif source
27
+ @source = StringIO.new(source)
28
+ elsif @source.nil?
29
+ raise AMFError, "no source to deserialize"
30
+ end
31
+
32
+ if @version == 0
33
+ @ref_cache = []
34
+ return amf0_deserialize
35
+ else
36
+ @string_cache = []
37
+ @object_cache = []
38
+ @trait_cache = []
39
+ return amf3_deserialize
40
+ end
41
+ end
42
+
43
+ # Reads an object from the deserializer's stream and returns it.
44
+ def read_object
45
+ if @version == 0
46
+ return amf0_deserialize
47
+ else
48
+ return amf3_deserialize
49
+ end
50
+ end
51
+
52
+ private
53
+ include RocketAMF::Pure::ReadIOHelpers
54
+
55
+ def amf0_deserialize type=nil
56
+ type = read_int8 @source unless type
57
+ case type
58
+ when AMF0_NUMBER_MARKER
59
+ amf0_read_number
60
+ when AMF0_BOOLEAN_MARKER
61
+ amf0_read_boolean
62
+ when AMF0_STRING_MARKER
63
+ amf0_read_string
64
+ when AMF0_OBJECT_MARKER
65
+ amf0_read_object
66
+ when AMF0_NULL_MARKER
67
+ nil
68
+ when AMF0_UNDEFINED_MARKER
69
+ nil
70
+ when AMF0_REFERENCE_MARKER
71
+ amf0_read_reference
72
+ when AMF0_HASH_MARKER
73
+ amf0_read_hash
74
+ when AMF0_STRICT_ARRAY_MARKER
75
+ amf0_read_array
76
+ when AMF0_DATE_MARKER
77
+ amf0_read_date
78
+ when AMF0_LONG_STRING_MARKER
79
+ amf0_read_string true
80
+ when AMF0_UNSUPPORTED_MARKER
81
+ nil
82
+ when AMF0_XML_MARKER
83
+ amf0_read_string true
84
+ when AMF0_TYPED_OBJECT_MARKER
85
+ amf0_read_typed_object
86
+ when AMF0_AMF3_MARKER
87
+ deserialize(3, nil)
88
+ else
89
+ raise AMFError, "Invalid type: #{type}"
90
+ end
91
+ end
92
+
93
+ def amf0_read_number
94
+ res = read_double @source
95
+ (res.is_a?(Float) && res.nan?) ? nil : res # check for NaN and convert them to nil
96
+ end
97
+
98
+ def amf0_read_boolean
99
+ read_int8(@source) != 0
100
+ end
101
+
102
+ def amf0_read_string long=false
103
+ len = long ? read_word32_network(@source) : read_word16_network(@source)
104
+ str = @source.read(len)
105
+ str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
106
+ str
107
+ end
108
+
109
+ def amf0_read_reference
110
+ index = read_word16_network(@source)
111
+ @ref_cache[index]
112
+ end
113
+
114
+ def amf0_read_array
115
+ len = read_word32_network(@source)
116
+ array = []
117
+ @ref_cache << array
118
+
119
+ 0.upto(len - 1) do
120
+ array << amf0_deserialize
121
+ end
122
+ array
123
+ end
124
+
125
+ def amf0_read_date
126
+ seconds = read_double(@source).to_f/1000
127
+ time = Time.at(seconds)
128
+ tz = read_word16_network(@source) # Unused
129
+ time
130
+ end
131
+
132
+ def amf0_read_props obj={}
133
+ while true
134
+ key = amf0_read_string
135
+ type = read_int8 @source
136
+ break if type == AMF0_OBJECT_END_MARKER
137
+ obj[key] = amf0_deserialize(type)
138
+ end
139
+ obj
140
+ end
141
+
142
+ def amf0_read_hash
143
+ len = read_word32_network(@source) # Read and ignore length
144
+ obj = {}
145
+ @ref_cache << obj
146
+ amf0_read_props obj
147
+ end
148
+
149
+ def amf0_read_object add_to_ref_cache=true
150
+ # Create "object" and add to ref cache (it's always a Hash)
151
+ obj = @class_mapper.get_ruby_obj ""
152
+ @ref_cache << obj
153
+
154
+ # Populate object
155
+ props = amf0_read_props
156
+ @class_mapper.populate_ruby_obj obj, props
157
+ return obj
158
+ end
159
+
160
+ def amf0_read_typed_object
161
+ # Create object to add to ref cache
162
+ class_name = amf0_read_string
163
+ obj = @class_mapper.get_ruby_obj class_name
164
+ @ref_cache << obj
165
+
166
+ # Populate object
167
+ props = amf0_read_props
168
+ @class_mapper.populate_ruby_obj obj, props
169
+ return obj
170
+ end
171
+
172
+ def amf3_deserialize
173
+ type = read_int8 @source
174
+ case type
175
+ when AMF3_UNDEFINED_MARKER
176
+ nil
177
+ when AMF3_NULL_MARKER
178
+ nil
179
+ when AMF3_FALSE_MARKER
180
+ false
181
+ when AMF3_TRUE_MARKER
182
+ true
183
+ when AMF3_INTEGER_MARKER
184
+ amf3_read_integer
185
+ when AMF3_DOUBLE_MARKER
186
+ amf3_read_number
187
+ when AMF3_STRING_MARKER
188
+ amf3_read_string
189
+ when AMF3_XML_DOC_MARKER, AMF3_XML_MARKER
190
+ amf3_read_xml
191
+ when AMF3_DATE_MARKER
192
+ amf3_read_date
193
+ when AMF3_ARRAY_MARKER
194
+ amf3_read_array
195
+ when AMF3_OBJECT_MARKER
196
+ amf3_read_object
197
+ when AMF3_BYTE_ARRAY_MARKER
198
+ amf3_read_byte_array
199
+ when AMF3_VECTOR_INT_MARKER, AMF3_VECTOR_UINT_MARKER, AMF3_VECTOR_DOUBLE_MARKER, AMF3_VECTOR_OBJECT_MARKER
200
+ amf3_read_vector type
201
+ when AMF3_DICT_MARKER
202
+ amf3_read_dict
203
+ else
204
+ raise AMFError, "Invalid type: #{type}"
205
+ end
206
+ end
207
+
208
+ def amf3_read_integer
209
+ n = 0
210
+ b = read_word8(@source) || 0
211
+ result = 0
212
+
213
+ while ((b & 0x80) != 0 && n < 3)
214
+ result = result << 7
215
+ result = result | (b & 0x7f)
216
+ b = read_word8(@source) || 0
217
+ n = n + 1
218
+ end
219
+
220
+ if (n < 3)
221
+ result = result << 7
222
+ result = result | b
223
+ else
224
+ #Use all 8 bits from the 4th byte
225
+ result = result << 8
226
+ result = result | b
227
+
228
+ #Check if the integer should be negative
229
+ if (result > MAX_INTEGER)
230
+ result -= (1 << 29)
231
+ end
232
+ end
233
+ result
234
+ end
235
+
236
+ def amf3_read_number
237
+ res = read_double @source
238
+ (res.is_a?(Float) && res.nan?) ? nil : res # check for NaN and convert them to nil
239
+ end
240
+
241
+ def amf3_read_string
242
+ type = amf3_read_integer
243
+ is_reference = (type & 0x01) == 0
244
+
245
+ if is_reference
246
+ reference = type >> 1
247
+ return @string_cache[reference]
248
+ else
249
+ length = type >> 1
250
+ str = ""
251
+ if length > 0
252
+ str = @source.read(length)
253
+ str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
254
+ @string_cache << str
255
+ end
256
+ return str
257
+ end
258
+ end
259
+
260
+ def amf3_read_xml
261
+ type = amf3_read_integer
262
+ is_reference = (type & 0x01) == 0
263
+
264
+ if is_reference
265
+ reference = type >> 1
266
+ return @object_cache[reference]
267
+ else
268
+ length = type >> 1
269
+ str = ""
270
+ if length > 0
271
+ str = @source.read(length)
272
+ str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
273
+ @object_cache << str
274
+ end
275
+ return str
276
+ end
277
+ end
278
+
279
+ def amf3_read_byte_array
280
+ type = amf3_read_integer
281
+ is_reference = (type & 0x01) == 0
282
+
283
+ if is_reference
284
+ reference = type >> 1
285
+ return @object_cache[reference]
286
+ else
287
+ length = type >> 1
288
+ obj = StringIO.new @source.read(length)
289
+ @object_cache << obj
290
+ obj
291
+ end
292
+ end
293
+
294
+ def amf3_read_array
295
+ type = amf3_read_integer
296
+ is_reference = (type & 0x01) == 0
297
+
298
+ if is_reference
299
+ reference = type >> 1
300
+ return @object_cache[reference]
301
+ else
302
+ length = type >> 1
303
+ property_name = amf3_read_string
304
+ array = property_name.length > 0 ? {} : []
305
+ @object_cache << array
306
+
307
+ while property_name.length > 0
308
+ value = amf3_deserialize
309
+ array[property_name] = value
310
+ property_name = amf3_read_string
311
+ end
312
+ 0.upto(length - 1) {|i| array[i] = amf3_deserialize }
313
+
314
+ array
315
+ end
316
+ end
317
+
318
+ def amf3_read_object
319
+ type = amf3_read_integer
320
+ is_reference = (type & 0x01) == 0
321
+
322
+ if is_reference
323
+ reference = type >> 1
324
+ return @object_cache[reference]
325
+ else
326
+ class_type = type >> 1
327
+ class_is_reference = (class_type & 0x01) == 0
328
+
329
+ if class_is_reference
330
+ reference = class_type >> 1
331
+ traits = @trait_cache[reference]
332
+ else
333
+ externalizable = (class_type & 0x02) != 0
334
+ dynamic = (class_type & 0x04) != 0
335
+ attribute_count = class_type >> 3
336
+ class_name = amf3_read_string
337
+
338
+ class_attributes = []
339
+ attribute_count.times{class_attributes << amf3_read_string} # Read class members
340
+
341
+ traits = {
342
+ :class_name => class_name,
343
+ :members => class_attributes,
344
+ :externalizable => externalizable,
345
+ :dynamic => dynamic
346
+ }
347
+ @trait_cache << traits
348
+ end
349
+
350
+ # Optimization for deserializing ArrayCollection
351
+ if traits[:class_name] == "flex.messaging.io.ArrayCollection"
352
+ arr = amf3_deserialize # Adds ArrayCollection array to object cache
353
+ @object_cache << arr # Add again for ArrayCollection source array
354
+ return arr
355
+ end
356
+
357
+ obj = @class_mapper.get_ruby_obj traits[:class_name]
358
+ @object_cache << obj
359
+
360
+ if traits[:externalizable]
361
+ obj.read_external self
362
+ else
363
+ props = {}
364
+ traits[:members].each do |key|
365
+ value = amf3_deserialize
366
+ props[key] = value
367
+ end
368
+
369
+ dynamic_props = nil
370
+ if traits[:dynamic]
371
+ dynamic_props = {}
372
+ while (key = amf3_read_string) && key.length != 0 do # read next key
373
+ value = amf3_deserialize
374
+ dynamic_props[key] = value
375
+ end
376
+ end
377
+
378
+ @class_mapper.populate_ruby_obj obj, props, dynamic_props
379
+ end
380
+ obj
381
+ end
382
+ end
383
+
384
+ def amf3_read_date
385
+ type = amf3_read_integer
386
+ is_reference = (type & 0x01) == 0
387
+ if is_reference
388
+ reference = type >> 1
389
+ return @object_cache[reference]
390
+ else
391
+ seconds = read_double(@source).to_f/1000
392
+ time = Time.at(seconds)
393
+ @object_cache << time
394
+ time
395
+ end
396
+ end
397
+
398
+ def amf3_read_dict
399
+ type = amf3_read_integer
400
+ is_reference = (type & 0x01) == 0
401
+ if is_reference
402
+ reference = type >> 1
403
+ return @object_cache[reference]
404
+ else
405
+ dict = {}
406
+ @object_cache << dict
407
+ length = type >> 1
408
+ weak_keys = read_int8 @source # Ignore: Not supported in ruby
409
+ 0.upto(length - 1) do |i|
410
+ dict[amf3_deserialize] = amf3_deserialize
411
+ end
412
+ dict
413
+ end
414
+ end
415
+
416
+ def amf3_read_vector vector_type
417
+ type = amf3_read_integer
418
+ is_reference = (type & 0x01) == 0
419
+ if is_reference
420
+ reference = type >> 1
421
+ return @object_cache[reference]
422
+ else
423
+ vec = []
424
+ @object_cache << vec
425
+ length = type >> 1
426
+ fixed_vector = read_int8 @source # Ignore
427
+ case vector_type
428
+ when AMF3_VECTOR_INT_MARKER
429
+ 0.upto(length - 1) do |i|
430
+ int = read_word32_network(@source)
431
+ int = int - 2**32 if int > MAX_INTEGER
432
+ vec << int
433
+ end
434
+ when AMF3_VECTOR_UINT_MARKER
435
+ 0.upto(length - 1) do |i|
436
+ vec << read_word32_network(@source)
437
+ puts vec[i].to_s(2)
438
+ end
439
+ when AMF3_VECTOR_DOUBLE_MARKER
440
+ 0.upto(length - 1) do |i|
441
+ vec << amf3_read_number
442
+ end
443
+ when AMF3_VECTOR_OBJECT_MARKER
444
+ vector_class = amf3_read_string # Ignore
445
+ puts vector_class
446
+ 0.upto(length - 1) do |i|
447
+ vec << amf3_deserialize
448
+ end
449
+ end
450
+ vec
451
+ end
452
+ end
453
+ end
454
+ end
455
+ end