rocketamf_pure 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 (91) hide show
  1. data/README.rdoc +47 -0
  2. data/Rakefile +9 -0
  3. data/benchmark.rb +73 -0
  4. data/lib/rocketamf.rb +212 -0
  5. data/lib/rocketamf/class_mapping.rb +237 -0
  6. data/lib/rocketamf/constants.rb +46 -0
  7. data/lib/rocketamf/ext.rb +28 -0
  8. data/lib/rocketamf/extensions.rb +22 -0
  9. data/lib/rocketamf/pure.rb +24 -0
  10. data/lib/rocketamf/pure/deserializer.rb +417 -0
  11. data/lib/rocketamf/pure/io_helpers.rb +94 -0
  12. data/lib/rocketamf/pure/remoting.rb +117 -0
  13. data/lib/rocketamf/pure/serializer.rb +474 -0
  14. data/lib/rocketamf/remoting.rb +196 -0
  15. data/lib/rocketamf/values/messages.rb +212 -0
  16. data/lib/rocketamf/values/typed_hash.rb +13 -0
  17. data/lib/rocketamf_pure.rb +1 -0
  18. data/spec/class_mapping_spec.rb +110 -0
  19. data/spec/deserializer_spec.rb +423 -0
  20. data/spec/fast_class_mapping_spec.rb +144 -0
  21. data/spec/fixtures/objects/amf0-boolean.bin +1 -0
  22. data/spec/fixtures/objects/amf0-complex-encoded-string.bin +0 -0
  23. data/spec/fixtures/objects/amf0-date.bin +0 -0
  24. data/spec/fixtures/objects/amf0-ecma-ordinal-array.bin +0 -0
  25. data/spec/fixtures/objects/amf0-hash.bin +0 -0
  26. data/spec/fixtures/objects/amf0-null.bin +1 -0
  27. data/spec/fixtures/objects/amf0-number.bin +0 -0
  28. data/spec/fixtures/objects/amf0-object.bin +0 -0
  29. data/spec/fixtures/objects/amf0-ref-test.bin +0 -0
  30. data/spec/fixtures/objects/amf0-strict-array.bin +0 -0
  31. data/spec/fixtures/objects/amf0-string.bin +0 -0
  32. data/spec/fixtures/objects/amf0-time.bin +0 -0
  33. data/spec/fixtures/objects/amf0-typed-object.bin +0 -0
  34. data/spec/fixtures/objects/amf0-undefined.bin +1 -0
  35. data/spec/fixtures/objects/amf0-untyped-object.bin +0 -0
  36. data/spec/fixtures/objects/amf0-xml-doc.bin +0 -0
  37. data/spec/fixtures/objects/amf3-0.bin +0 -0
  38. data/spec/fixtures/objects/amf3-array-collection.bin +2 -0
  39. data/spec/fixtures/objects/amf3-array-ref.bin +1 -0
  40. data/spec/fixtures/objects/amf3-associative-array.bin +1 -0
  41. data/spec/fixtures/objects/amf3-bigNum.bin +0 -0
  42. data/spec/fixtures/objects/amf3-byte-array-ref.bin +1 -0
  43. data/spec/fixtures/objects/amf3-byte-array.bin +0 -0
  44. data/spec/fixtures/objects/amf3-complex-array-collection.bin +6 -0
  45. data/spec/fixtures/objects/amf3-complex-encoded-string-array.bin +1 -0
  46. data/spec/fixtures/objects/amf3-date-ref.bin +0 -0
  47. data/spec/fixtures/objects/amf3-date.bin +0 -0
  48. data/spec/fixtures/objects/amf3-dictionary.bin +0 -0
  49. data/spec/fixtures/objects/amf3-dynamic-object.bin +2 -0
  50. data/spec/fixtures/objects/amf3-empty-array-ref.bin +1 -0
  51. data/spec/fixtures/objects/amf3-empty-array.bin +1 -0
  52. data/spec/fixtures/objects/amf3-empty-dictionary.bin +0 -0
  53. data/spec/fixtures/objects/amf3-empty-string-ref.bin +1 -0
  54. data/spec/fixtures/objects/amf3-encoded-string-ref.bin +0 -0
  55. data/spec/fixtures/objects/amf3-externalizable.bin +0 -0
  56. data/spec/fixtures/objects/amf3-false.bin +1 -0
  57. data/spec/fixtures/objects/amf3-float.bin +0 -0
  58. data/spec/fixtures/objects/amf3-graph-member.bin +0 -0
  59. data/spec/fixtures/objects/amf3-hash.bin +2 -0
  60. data/spec/fixtures/objects/amf3-large-max.bin +0 -0
  61. data/spec/fixtures/objects/amf3-large-min.bin +0 -0
  62. data/spec/fixtures/objects/amf3-max.bin +1 -0
  63. data/spec/fixtures/objects/amf3-min.bin +0 -0
  64. data/spec/fixtures/objects/amf3-mixed-array.bin +10 -0
  65. data/spec/fixtures/objects/amf3-null.bin +1 -0
  66. data/spec/fixtures/objects/amf3-object-ref.bin +0 -0
  67. data/spec/fixtures/objects/amf3-primitive-array.bin +1 -0
  68. data/spec/fixtures/objects/amf3-string-ref.bin +0 -0
  69. data/spec/fixtures/objects/amf3-string.bin +1 -0
  70. data/spec/fixtures/objects/amf3-symbol.bin +1 -0
  71. data/spec/fixtures/objects/amf3-trait-ref.bin +3 -0
  72. data/spec/fixtures/objects/amf3-true.bin +1 -0
  73. data/spec/fixtures/objects/amf3-typed-object.bin +2 -0
  74. data/spec/fixtures/objects/amf3-xml-doc.bin +1 -0
  75. data/spec/fixtures/objects/amf3-xml-ref.bin +1 -0
  76. data/spec/fixtures/objects/amf3-xml.bin +1 -0
  77. data/spec/fixtures/request/acknowledge-response.bin +0 -0
  78. data/spec/fixtures/request/amf0-error-response.bin +0 -0
  79. data/spec/fixtures/request/blaze-response.bin +0 -0
  80. data/spec/fixtures/request/commandMessage.bin +0 -0
  81. data/spec/fixtures/request/flex-request.bin +0 -0
  82. data/spec/fixtures/request/multiple-simple-request.bin +0 -0
  83. data/spec/fixtures/request/remotingMessage.bin +0 -0
  84. data/spec/fixtures/request/simple-request.bin +0 -0
  85. data/spec/fixtures/request/simple-response.bin +0 -0
  86. data/spec/fixtures/request/unsupportedCommandMessage.bin +0 -0
  87. data/spec/messages_spec.rb +39 -0
  88. data/spec/remoting_spec.rb +196 -0
  89. data/spec/serializer_spec.rb +503 -0
  90. data/spec/spec_helper.rb +55 -0
  91. metadata +148 -0
@@ -0,0 +1,46 @@
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_DICT_MARKER = 0x11 #"\021"
37
+
38
+ # Other AMF3 Markers
39
+ AMF3_EMPTY_STRING = 0x01
40
+ AMF3_CLOSE_DYNAMIC_OBJECT = 0x01
41
+ AMF3_CLOSE_DYNAMIC_ARRAY = 0x01
42
+
43
+ # Other Constants
44
+ MAX_INTEGER = 268435455
45
+ MIN_INTEGER = -268435456
46
+ 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,417 @@
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={}, key_type=:sym
133
+ while true
134
+ key = amf0_read_string
135
+ type = read_int8 @source
136
+ break if type == AMF0_OBJECT_END_MARKER
137
+ key = key.to_sym if key_type == :sym
138
+ obj[key] = amf0_deserialize(type)
139
+ end
140
+ obj
141
+ end
142
+
143
+ def amf0_read_hash
144
+ len = read_word32_network(@source) # Read and ignore length
145
+ obj = {}
146
+ @ref_cache << obj
147
+ amf0_read_props obj, :string
148
+ end
149
+
150
+ def amf0_read_object add_to_ref_cache=true
151
+ # Create "object" and add to ref cache (it's always a Hash)
152
+ obj = @class_mapper.get_ruby_obj ""
153
+ @ref_cache << obj
154
+
155
+ # Populate object
156
+ props = amf0_read_props
157
+ @class_mapper.populate_ruby_obj obj, props
158
+ return obj
159
+ end
160
+
161
+ def amf0_read_typed_object
162
+ # Create object to add to ref cache
163
+ class_name = amf0_read_string
164
+ obj = @class_mapper.get_ruby_obj class_name
165
+ @ref_cache << obj
166
+
167
+ # Populate object
168
+ props = amf0_read_props
169
+ @class_mapper.populate_ruby_obj obj, props
170
+ return obj
171
+ end
172
+
173
+ def amf3_deserialize
174
+ type = read_int8 @source
175
+ case type
176
+ when AMF3_UNDEFINED_MARKER
177
+ nil
178
+ when AMF3_NULL_MARKER
179
+ nil
180
+ when AMF3_FALSE_MARKER
181
+ false
182
+ when AMF3_TRUE_MARKER
183
+ true
184
+ when AMF3_INTEGER_MARKER
185
+ amf3_read_integer
186
+ when AMF3_DOUBLE_MARKER
187
+ amf3_read_number
188
+ when AMF3_STRING_MARKER
189
+ amf3_read_string
190
+ when AMF3_XML_DOC_MARKER, AMF3_XML_MARKER
191
+ amf3_read_xml
192
+ when AMF3_DATE_MARKER
193
+ amf3_read_date
194
+ when AMF3_ARRAY_MARKER
195
+ amf3_read_array
196
+ when AMF3_OBJECT_MARKER
197
+ amf3_read_object
198
+ when AMF3_BYTE_ARRAY_MARKER
199
+ amf3_read_byte_array
200
+ when AMF3_DICT_MARKER
201
+ amf3_read_dict
202
+ else
203
+ raise AMFError, "Invalid type: #{type}"
204
+ end
205
+ end
206
+
207
+ def amf3_read_integer
208
+ n = 0
209
+ b = read_word8(@source) || 0
210
+ result = 0
211
+
212
+ while ((b & 0x80) != 0 && n < 3)
213
+ result = result << 7
214
+ result = result | (b & 0x7f)
215
+ b = read_word8(@source) || 0
216
+ n = n + 1
217
+ end
218
+
219
+ if (n < 3)
220
+ result = result << 7
221
+ result = result | b
222
+ else
223
+ #Use all 8 bits from the 4th byte
224
+ result = result << 8
225
+ result = result | b
226
+
227
+ #Check if the integer should be negative
228
+ if (result > MAX_INTEGER)
229
+ result -= (1 << 29)
230
+ end
231
+ end
232
+ result
233
+ end
234
+
235
+ def amf3_read_number
236
+ res = read_double @source
237
+ (res.is_a?(Float) && res.nan?) ? nil : res # check for NaN and convert them to nil
238
+ end
239
+
240
+ def amf3_read_string
241
+ type = amf3_read_integer
242
+ is_reference = (type & 0x01) == 0
243
+
244
+ if is_reference
245
+ reference = type >> 1
246
+ return @string_cache[reference]
247
+ else
248
+ length = type >> 1
249
+ str = ""
250
+ if length > 0
251
+ str = @source.read(length)
252
+ str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
253
+ @string_cache << str
254
+ end
255
+ return str
256
+ end
257
+ end
258
+
259
+ def amf3_read_xml
260
+ type = amf3_read_integer
261
+ is_reference = (type & 0x01) == 0
262
+
263
+ if is_reference
264
+ reference = type >> 1
265
+ return @object_cache[reference]
266
+ else
267
+ length = type >> 1
268
+ str = ""
269
+ if length > 0
270
+ str = @source.read(length)
271
+ str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
272
+ @object_cache << str
273
+ end
274
+ return str
275
+ end
276
+ end
277
+
278
+ def amf3_read_byte_array
279
+ type = amf3_read_integer
280
+ is_reference = (type & 0x01) == 0
281
+
282
+ if is_reference
283
+ reference = type >> 1
284
+ return @object_cache[reference]
285
+ else
286
+ length = type >> 1
287
+ obj = StringIO.new @source.read(length)
288
+ @object_cache << obj
289
+ obj
290
+ end
291
+ end
292
+
293
+ def amf3_read_array
294
+ type = amf3_read_integer
295
+ is_reference = (type & 0x01) == 0
296
+
297
+ if is_reference
298
+ reference = type >> 1
299
+ return @object_cache[reference]
300
+ else
301
+ length = type >> 1
302
+ property_name = amf3_read_string
303
+ array = property_name.length > 0 ? {} : []
304
+ @object_cache << array
305
+
306
+ while property_name.length > 0
307
+ value = amf3_deserialize
308
+ array[property_name] = value
309
+ property_name = amf3_read_string
310
+ end
311
+ 0.upto(length - 1) {|i| array[i] = amf3_deserialize }
312
+
313
+ array
314
+ end
315
+ end
316
+
317
+ def amf3_read_object
318
+ type = amf3_read_integer
319
+ is_reference = (type & 0x01) == 0
320
+
321
+ if is_reference
322
+ reference = type >> 1
323
+ return @object_cache[reference]
324
+ else
325
+ class_type = type >> 1
326
+ class_is_reference = (class_type & 0x01) == 0
327
+
328
+ if class_is_reference
329
+ reference = class_type >> 1
330
+ traits = @trait_cache[reference]
331
+ else
332
+ externalizable = (class_type & 0x02) != 0
333
+ dynamic = (class_type & 0x04) != 0
334
+ attribute_count = class_type >> 3
335
+ class_name = amf3_read_string
336
+
337
+ class_attributes = []
338
+ attribute_count.times{class_attributes << amf3_read_string} # Read class members
339
+
340
+ traits = {
341
+ :class_name => class_name,
342
+ :members => class_attributes,
343
+ :externalizable => externalizable,
344
+ :dynamic => dynamic
345
+ }
346
+ @trait_cache << traits
347
+ end
348
+
349
+ # Optimization for deserializing ArrayCollection
350
+ if traits[:class_name] == "flex.messaging.io.ArrayCollection"
351
+ arr = amf3_deserialize # Adds ArrayCollection array to object cache
352
+ @object_cache << arr # Add again for ArrayCollection source array
353
+ return arr
354
+ end
355
+
356
+ obj = @class_mapper.get_ruby_obj traits[:class_name]
357
+ @object_cache << obj
358
+
359
+ if traits[:externalizable]
360
+ obj.read_external self
361
+ else
362
+ props = {}
363
+ traits[:members].each do |key|
364
+ value = amf3_deserialize
365
+ props[key.to_sym] = value
366
+ end
367
+
368
+ dynamic_props = nil
369
+ if traits[:dynamic]
370
+ dynamic_props = {}
371
+ while (key = amf3_read_string) && key.length != 0 do # read next key
372
+ value = amf3_deserialize
373
+ dynamic_props[key.to_sym] = value
374
+ end
375
+ end
376
+
377
+ @class_mapper.populate_ruby_obj obj, props, dynamic_props
378
+ end
379
+ obj
380
+ end
381
+ end
382
+
383
+ def amf3_read_date
384
+ type = amf3_read_integer
385
+ is_reference = (type & 0x01) == 0
386
+ if is_reference
387
+ reference = type >> 1
388
+ return @object_cache[reference]
389
+ else
390
+ seconds = read_double(@source).to_f/1000
391
+ time = Time.at(seconds)
392
+ @object_cache << time
393
+ time
394
+ end
395
+ end
396
+
397
+ def amf3_read_dict
398
+ type = amf3_read_integer
399
+ # Currently duplicate dictionaries send false, but I'll leave this in here just in case
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
+ skip = amf3_read_integer # TODO: Handle when specs are updated
409
+ 0.upto(length - 1) do |i|
410
+ dict[amf3_deserialize] = amf3_deserialize
411
+ end
412
+ dict
413
+ end
414
+ end
415
+ end
416
+ end
417
+ end