class AMF::Pure::Serializer

Pure ruby serializer for AMF3

Public Class Methods

new(class_mapper) click to toggle source

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

   # File lib/amf/pure/serializer.rb
26 def initialize(class_mapper)
27   @class_mapper = class_mapper
28   @stream       = ''
29   @depth        = 0
30 end

Public Instance Methods

serialize(obj) click to toggle source

Serialize the given object using AMF3. Can be called from inside encode_amf.

   # File lib/amf/pure/serializer.rb
34 def serialize(obj)
35   # Initialize caches
36   if @depth == 0
37     @cache_strings = CacheStrings.new
38     @cache_objects = CacheObjects.new
39     @cache_traits  = CacheStrings.new
40   end
41   @depth += 1
42 
43   # Perform serialization
44   amf3_serialize(obj)
45 
46   # Cleanup
47   @depth -= 1
48 
49   if @depth == 0
50     @cache_strings = nil
51     @cache_objects = nil
52     @cache_traits  = nil
53   end
54 
55   @stream
56 end
write_array(value) click to toggle source

Helper for writing arrays inside encode_amf.

   # File lib/amf/pure/serializer.rb
90 def write_array(value)
91   amf3_write_array(value)
92 end
write_object(obj, props = nil, traits = nil) click to toggle source

Helper for writing objects inside encode_amf. If you pass in a property hash, it will use it rather than having the class mapper determine properties. You can also specify a traits hash, which can be used to reduce serialized data size or serialize things as externalizable.

    # File lib/amf/pure/serializer.rb
 99 def write_object(obj, props = nil, traits = nil)
100   amf3_write_object(obj, props, traits)
101 end

Private Instance Methods

amf3_serialize(object) click to toggle source
   # File lib/amf/pure/serializer.rb
59 def amf3_serialize(object)
60   case true
61     when object.is_a?(NilClass)
62       amf3_write_null
63     when object.respond_to?(:encode_amf)
64       object.encode_amf(self)
65     when object.is_a?(TrueClass)
66       amf3_write_true
67     when object.is_a?(FalseClass)
68       amf3_write_false
69     when object.is_a?(Numeric)
70       amf3_write_numeric(object)
71     when object.is_a?(Symbol), object.is_a?(String)
72       amf3_write_string(object.to_s)
73     when object.is_a?(Time)
74       amf3_write_time object
75     when object.is_a?(Date)
76       amf3_write_date(object)
77     when object.is_a?(StringIO)
78       amf3_write_byte_array(object)
79     when object.is_a?(Array)
80       amf3_write_array(object)
81     when object.is_a?(Hash), object.is_a?(Object)
82       amf3_write_object(object)
83     else
84       raise AMFError, 'unknown type for serialize'
85   end
86 end
amf3_write_array(value) click to toggle source
    # File lib/amf/pure/serializer.rb
187 def amf3_write_array(value)
188   # Write type marker
189   @stream << AMF3_MARKER_ARRAY
190 
191   # Write reference or cache array
192   if @cache_objects[value] != nil
193     amf3_write_reference(@cache_objects[value])
194     return
195   end
196 
197   @cache_objects.add_object(value)
198 
199   # Build AMF string for array
200   header = value.length << 1 # make room for a low bit of 1
201   header = header | 1 # set the low bit to 1
202   @stream << pack_integer(header)
203 
204   @stream << AMF3_CLOSE_DYNAMIC_ARRAY
205 
206   value.each do |elem|
207     amf3_serialize elem
208   end
209 end
amf3_write_byte_array(value) click to toggle source
    # File lib/amf/pure/serializer.rb
173 def amf3_write_byte_array(value)
174   @stream << AMF3_MARKER_BYTE_ARRAY
175 
176   if @cache_objects[value] != nil
177     amf3_write_reference(@cache_objects[value])
178   else
179     @cache_objects.add_object(value)
180     str = value.string
181     @stream << pack_integer(str.bytesize << 1 | 1)
182     @stream << str
183   end
184 end
amf3_write_date(value) click to toggle source
    # File lib/amf/pure/serializer.rb
157 def amf3_write_date(value)
158   @stream << AMF3_MARKER_DATE
159 
160   if @cache_objects[value] != nil
161     amf3_write_reference(@cache_objects[value])
162   else
163     # Cache date
164     @cache_objects.add_object(value)
165 
166     # Build AMF string
167     @stream << AMF3_MARKER_NULL
168     @stream << pack_double(value.strftime('%Q').to_i)
169   end
170 end
amf3_write_false() click to toggle source
    # File lib/amf/pure/serializer.rb
122 def amf3_write_false
123   # no data is serialized except their type marker
124   @stream << AMF3_MARKER_FALSE
125 end
amf3_write_null() click to toggle source
    # File lib/amf/pure/serializer.rb
110 def amf3_write_null
111   # no data is serialized except their type marker
112   @stream << AMF3_MARKER_NULL
113 end
amf3_write_numeric(value) click to toggle source
    # File lib/amf/pure/serializer.rb
128 def amf3_write_numeric(value)
129   if !value.integer? || value < INTEGER_MIN || value > INTEGER_MAX # Check valid range for 29 bits
130     @stream << AMF3_MARKER_DOUBLE
131     @stream << pack_double(value)
132   else
133     @stream << AMF3_MARKER_INTEGER
134     @stream << pack_integer(value)
135   end
136 end
amf3_write_object(value, properties = nil, traits = nil) click to toggle source
    # File lib/amf/pure/serializer.rb
212 def amf3_write_object(value, properties = nil, traits = nil)
213   @stream << AMF3_MARKER_OBJECT
214 
215   # Caching...
216   if @cache_objects[value] != nil
217     amf3_write_reference(@cache_objects[value])
218     return
219   end
220 
221   @cache_objects.add_object(value)
222 
223   # Calculate traits if not given
224   use_default_class_name = false
225 
226   if traits.nil?
227     traits     =
228         {
229             class_name: @class_mapper.get_class_name_remote(value),
230             members:    [],
231             dynamic:    true
232         }
233     use_default_class_name = true unless traits[:class_name]
234   end
235 
236   class_name = use_default_class_name ? '__default__' : traits[:class_name]
237 
238   # Write out traits
239   if !class_name.nil? && @cache_traits[class_name] != nil
240     @stream << pack_integer(@cache_traits[class_name] << 2 | 0x01)
241   else
242     @cache_traits.add_string(class_name) unless class_name.nil?
243 
244     # Write out trait header
245     header = 0x03 # Not object ref and not trait ref
246     header |= 0x02 << 2 if traits[:dynamic]
247     header |= traits[:members].length << 4
248     @stream << pack_integer(header)
249 
250     # Write out class name
251     if use_default_class_name
252       amf3_write_string_internal('')
253     else
254       amf3_write_string_internal(class_name.to_s)
255     end
256 
257     # Write out members
258     traits[:members].each { |m| amf3_write_string_internal(m) }
259   end
260 
261   # Extract properties if not given
262   properties = @class_mapper.object_serialize(value) if properties.nil?
263 
264   # Write out sealed properties
265   traits[:members].each do |m|
266     amf3_serialize(properties[m])
267     properties.delete(m)
268   end
269 
270   # Write out dynamic properties
271   if traits[:dynamic]
272     # Write out dynamic properties
273     properties.each do |key, val|
274       amf3_write_string_internal(key.to_s)
275       amf3_serialize(val)
276     end
277 
278     # Write close
279     @stream << AMF3_CLOSE_DYNAMIC_OBJECT
280   end
281 end
amf3_write_reference(index) click to toggle source
    # File lib/amf/pure/serializer.rb
104 def amf3_write_reference(index)
105   header = index << 1 # shift value left to leave a low bit of 0
106   @stream << pack_integer(header)
107 end
amf3_write_string(value) click to toggle source
    # File lib/amf/pure/serializer.rb
284 def amf3_write_string(value)
285   @stream << AMF3_MARKER_STRING
286   amf3_write_string_internal value
287 end
amf3_write_string_internal(value) click to toggle source
    # File lib/amf/pure/serializer.rb
291 def amf3_write_string_internal(value)
292   if value.respond_to?(:encode)
293     value = value.dup if value.frozen?
294 
295     value = value.encode('UTF-8')
296 
297     value.force_encoding('ASCII-8BIT')
298   end
299 
300   if value == ''
301     @stream << AMF3_EMPTY_STRING
302   elsif @cache_strings[value] != nil
303     amf3_write_reference(@cache_strings[value])
304   else
305 
306     # Cache string
307     @cache_strings.add_string(value)
308 
309     # Build AMF string
310     @stream << pack_integer(value.bytesize << 1 | 1)
311     @stream << value
312   end
313 end
amf3_write_time(value) click to toggle source
    # File lib/amf/pure/serializer.rb
139 def amf3_write_time(value)
140   @stream << AMF3_MARKER_DATE
141 
142   if @cache_objects[value] != nil
143     amf3_write_reference(@cache_objects[value])
144   else
145     # Cache time
146     @cache_objects.add_object(value)
147 
148     # Build AMF string
149     value = value.getutc # Dup and convert to UTC
150     milli = (value.to_f * 1000).to_i
151     @stream << AMF3_MARKER_NULL
152     @stream << pack_double(milli)
153   end
154 end
amf3_write_true() click to toggle source
    # File lib/amf/pure/serializer.rb
116 def amf3_write_true
117   # no data is serialized except their type marker
118   @stream << AMF3_MARKER_TRUE
119 end