rocketamf_pure 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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