class AMF::Pure::Deserializer

Pure ruby deserializer for AMF3 requests

Attributes

source[R]

Used for read_object

Public Class Methods

new(class_mapper) click to toggle source

Pass in the class mapper instance to use when deserializing. This enables better caching behavior in the class mapper and allows one to change mappings between deserialization attempts.

   # File lib/amf/pure/deserializer.rb
25 def initialize(class_mapper)
26   @class_mapper = class_mapper
27 end

Public Instance Methods

deserialize(source) click to toggle source

Deserialize the source using AMF3. Source should either be a string or StringIO object. If you pass a StringIO object, it will have its position updated to the end of the deserialized data. raise AMFError if error appeared in deserialize, source is nil raise AMFErrorIncomplete if source is incomplete return hash {objects: [], incomplete_objects: String}

   # File lib/amf/pure/deserializer.rb
37 def deserialize(source)
38   raise AMFError, 'no source to deserialize' if source.nil?
39 
40   @source = source.is_a?(StringIO) ? source : StringIO.new(source)
41 
42   objects = []
43 
44   incomplete_objects = nil
45 
46   until @source.eof?
47 
48     @cache_strings = []
49     @cache_objects = []
50     @cache_traits  = []
51 
52     source_position = @source.pos
53 
54     begin
55       objects << amf3_deserialize
56     rescue AMFErrorIncomplete, AMFError => e
57 
58       @source.pos = source_position
59 
60       incomplete_objects = @source.read
61 
62       break
63     end
64   end
65 
66   {
67       objects:            objects,
68       incomplete_objects: incomplete_objects
69   }
70 end
read_object() click to toggle source

Reads an object from the deserializer stream and returns it.

   # File lib/amf/pure/deserializer.rb
74 def read_object
75   amf3_deserialize
76 end

Private Instance Methods

amf3_deserialize() click to toggle source
    # File lib/amf/pure/deserializer.rb
 79 def amf3_deserialize
 80   type = read_int8(@source)
 81 
 82   case type
 83     when AMF3_MARKER_UNDEFINED
 84       nil
 85     when AMF3_MARKER_NULL
 86       nil
 87     when AMF3_MARKER_FALSE
 88       false
 89     when AMF3_MARKER_TRUE
 90       true
 91     when AMF3_MARKER_INTEGER
 92       amf3_read_integer
 93     when AMF3_MARKER_DOUBLE
 94       amf3_read_number
 95     when AMF3_MARKER_STRING
 96       amf3_read_string
 97     when AMF3_MARKER_DATE
 98       amf3_read_date
 99     when AMF3_MARKER_ARRAY
100       amf3_read_array
101     when AMF3_MARKER_OBJECT
102       amf3_read_object
103     when AMF3_MARKER_BYTE_ARRAY
104       amf3_read_byte_array
105     when AMF3_MARKER_VECTOR_INT, AMF3_MARKER_VECTOR_UINT, AMF3_MARKER_VECTOR_DOUBLE, AMF3_MARKER_VECTOR_OBJECT, AMF3_MARKER_XML_DOC, AMF3_MARKER_XML
106       raise AMFError, "Unsupported type: #{type}"
107     when AMF3_MARKER_DICTIONARY
108       amf3_read_dictionary
109     else
110       raise AMFError, "Invalid type: #{type}"
111   end
112 end
amf3_read_array() click to toggle source
    # File lib/amf/pure/deserializer.rb
226 def amf3_read_array
227   result = nil
228 
229   type = amf3_read_integer
230 
231   result = get_as_reference_object(type)
232 
233   if result.nil?
234     length        = type >> 1
235     property_name = amf3_read_string
236     result        = property_name.length > 0 ? {} : []
237     @cache_objects << result
238 
239     while property_name.length > 0
240       value                 = amf3_deserialize
241       result[property_name] = value
242       property_name         = amf3_read_string
243     end
244 
245     0.upto(length - 1) { |i| result[i] = amf3_deserialize }
246   end
247 
248   result
249 end
amf3_read_byte_array() click to toggle source
    # File lib/amf/pure/deserializer.rb
308 def amf3_read_byte_array
309   result = nil
310 
311   type = amf3_read_integer
312 
313   result = get_as_reference_object(type)
314 
315   if result.nil?
316     length = type >> 1
317 
318     if length > (@source.size - @source.pos)
319       raise AMFErrorIncomplete.new
320     end
321 
322     result = StringIO.new(@source.read(length))
323     @cache_objects << result
324   end
325 
326   result
327 end
amf3_read_date() click to toggle source
    # File lib/amf/pure/deserializer.rb
209 def amf3_read_date
210   result = nil
211 
212   type = amf3_read_integer
213 
214   result = get_as_reference_object(type)
215 
216   if result.nil?
217     seconds = read_double(@source).to_f / 1000
218     result  = Time.at(seconds)
219     @cache_objects << result
220   end
221 
222   result
223 end
amf3_read_dictionary() click to toggle source
    # File lib/amf/pure/deserializer.rb
330 def amf3_read_dictionary
331   result = nil
332 
333   type = amf3_read_integer
334 
335   result = get_as_reference_object(type)
336 
337   if result.nil?
338     result = {}
339     @cache_objects << result
340     length    = type >> 1
341     weak_keys = read_int8(@source) # Ignore: Not supported in ruby
342 
343     0.upto(length - 1) do |i|
344       result[amf3_deserialize] = amf3_deserialize
345     end
346 
347   end
348 
349   result
350 end
amf3_read_integer() click to toggle source
    # File lib/amf/pure/deserializer.rb
139 def amf3_read_integer
140   result = 0
141 
142   n = 0
143   b = read_word8(@source) || 0
144 
145   while (b & 0x80) != 0 && n < 3
146     result = result << 7
147     result = result | (b & 0x7f)
148     b      = read_word8(@source) || 0
149     n      = n + 1
150   end
151 
152   if n < 3
153     result = result << 7
154     result = result | b
155   else
156     #Use all 8 bits from the 4th byte
157     result = result << 8
158     result = result | b
159 
160     #Check if the integer should be negative
161     if result > INTEGER_MAX
162       result -= (1 << 29)
163     end
164   end
165 
166   result
167 end
amf3_read_number() click to toggle source
    # File lib/amf/pure/deserializer.rb
170 def amf3_read_number
171   result = read_double(@source)
172 
173   #check for NaN and convert them to nil
174   if result.is_a?(Float) && result.nan?
175     result = nil
176   end
177 
178   result
179 end
amf3_read_object() click to toggle source

dynamic - c an instance of a Class definition with the dynamic trait declared; public variable members can be added and removed from instances dynamically at runtime

    # File lib/amf/pure/deserializer.rb
253 def amf3_read_object
254   result = nil
255 
256   type = amf3_read_integer
257 
258   result = get_as_reference_object(type)
259 
260   if result.nil?
261     class_type         = type >> 1
262     class_is_reference = (class_type & 0x01) == 0
263 
264     if class_is_reference
265       reference = class_type >> 1
266       traits    = @cache_traits[reference]
267     else
268       dynamic         = (class_type & 0x04) != 0
269       attribute_count = class_type >> 3
270       class_name      = amf3_read_string
271 
272       class_attributes = []
273       attribute_count.times { class_attributes << amf3_read_string } # Read class members
274 
275       traits =
276           {
277               class_name: class_name,
278               members:    class_attributes,
279               dynamic:    dynamic
280           }
281       @cache_traits << traits
282     end
283 
284     result = @class_mapper.create_object(traits[:class_name])
285     @cache_objects << result
286 
287     properties = {}
288 
289     traits[:members].each do |key|
290       value           = amf3_deserialize
291       properties[key] = value
292     end
293 
294     if traits[:dynamic]
295       while (key = amf3_read_string) && key.length != 0 do # read next key
296         value           = amf3_deserialize
297         properties[key] = value
298       end
299     end
300 
301     @class_mapper.object_deserialize(result, properties)
302   end
303 
304   result
305 end
amf3_read_string() click to toggle source
    # File lib/amf/pure/deserializer.rb
182 def amf3_read_string
183   result = nil
184 
185   type = amf3_read_integer
186 
187   result = get_as_reference_string(type)
188 
189   if result.nil?
190     length = type >> 1
191     result = ''
192 
193     if length > 0
194 
195       if length > (@source.size - @source.pos)
196         raise AMFErrorIncomplete.new
197       end
198 
199       result = @source.read(length)
200       result.force_encoding('UTF-8') if result.respond_to?(:force_encoding)
201       @cache_strings << result
202     end
203   end
204 
205   result
206 end
get_as_reference_object(type) click to toggle source
    # File lib/amf/pure/deserializer.rb
115 def get_as_reference_object(type)
116   result = nil
117 
118   if (type & 0x01) == 0 #is reference?
119     reference = type >> 1
120     result    = @cache_objects[reference]
121   end
122 
123   result
124 end
get_as_reference_string(type) click to toggle source
    # File lib/amf/pure/deserializer.rb
127 def get_as_reference_string(type)
128   result = nil
129 
130   if (type & 0x01) == 0 #is reference?
131     reference = type >> 1
132     result    = @cache_strings[reference]
133   end
134 
135   result
136 end