class RocketAMF::Pure::Serializer

Pure ruby serializer for AMF3

Attributes

stream[R]

Properties

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/rocketamf/pure/serializer.rb
28 def initialize(class_mapper)
29   @class_mapper = class_mapper
30   @stream       = ''
31   @depth        = 0
32 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/rocketamf/pure/serializer.rb
36 def serialize(obj)
37   # Initialize caches
38   if @depth == 0
39     @string_cache = StringCache.new
40     @object_cache = ObjectCache.new
41     @trait_cache  = StringCache.new
42   end
43   @depth += 1
44 
45   # Perform serialization
46   amf3_serialize(obj)
47 
48   # Cleanup
49   @depth -= 1
50   if @depth == 0
51     @ref_cache    = nil
52     @string_cache = nil
53     @object_cache = nil
54     @trait_cache  = nil
55   end
56 
57   @stream
58 end
write_array(value) click to toggle source

Helper for writing arrays inside encode_amf.

   # File lib/rocketamf/pure/serializer.rb
61 def write_array(value)
62   amf3_write_array value
63 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/rocketamf/pure/serializer.rb
69 def write_object(obj, props = nil, traits = nil)
70   amf3_write_object(obj, props, traits)
71 end

Private Instance Methods

amf3_serialize(obj) click to toggle source
   # File lib/rocketamf/pure/serializer.rb
75 def amf3_serialize(obj)
76   if obj.respond_to?(:encode_amf)
77     obj.encode_amf(self)
78   elsif obj.is_a?(NilClass)
79     amf3_write_null
80   elsif obj.is_a?(TrueClass)
81     amf3_write_true
82   elsif obj.is_a?(FalseClass)
83     amf3_write_false
84   elsif obj.is_a?(Numeric)
85     amf3_write_numeric(obj)
86   elsif obj.is_a?(Symbol) || obj.is_a?(String)
87     amf3_write_string(obj.to_s)
88   elsif obj.is_a?(Time)
89     amf3_write_time obj
90   elsif obj.is_a?(Date)
91     amf3_write_date(obj)
92   elsif obj.is_a?(StringIO)
93     amf3_write_byte_array(obj)
94   elsif obj.is_a?(Array)
95     amf3_write_array(obj)
96   elsif obj.is_a?(Hash) || obj.is_a?(Object)
97     amf3_write_object(obj)
98   end
99 end
amf3_write_array(value) click to toggle source
    # File lib/rocketamf/pure/serializer.rb
188 def amf3_write_array(value)
189   # Is it an array collection?
190   is_array_collection = false
191 
192   if value.respond_to?(:is_array_collection?)
193     is_array_collection = value.is_array_collection?
194   else
195     is_array_collection = @class_mapper.use_array_collection
196   end
197 
198   # Write type marker
199   @stream << (is_array_collection ? AMF3_OBJECT_MARKER : AMF3_ARRAY_MARKER)
200 
201   # Write reference or cache array
202   if @object_cache[value] != nil
203     amf3_write_reference(@object_cache[value])
204     return
205   else
206     @object_cache.add_obj(value)
207     @object_cache.add_obj(nil) if is_array_collection # The array collection source array
208   end
209 
210   # Write out traits and array marker if it's an array collection
211   if is_array_collection
212     class_name = 'flex.messaging.io.ArrayCollection'
213 
214     if @trait_cache[class_name] != nil
215       @stream << pack_integer(@trait_cache[class_name] << 2 | 0x01)
216     else
217       @trait_cache.add_obj(class_name)
218       @stream << "\a" # Externalizable, non-dynamic
219       amf3_write_utf8_vr(class_name)
220     end
221     @stream << AMF3_ARRAY_MARKER
222   end
223 
224   # Build AMF string for array
225   header = value.length << 1 # make room for a low bit of 1
226   header = header | 1 # set the low bit to 1
227   @stream << pack_integer(header)
228   @stream << AMF3_CLOSE_DYNAMIC_ARRAY
229   value.each do |elem|
230     amf3_serialize elem
231   end
232 end
amf3_write_byte_array(value) click to toggle source
    # File lib/rocketamf/pure/serializer.rb
174 def amf3_write_byte_array(value)
175   @stream << AMF3_BYTE_ARRAY_MARKER
176 
177   if @object_cache[value] != nil
178     amf3_write_reference(@object_cache[value])
179   else
180     @object_cache.add_obj(value)
181     str = value.string
182     @stream << pack_integer(str.bytesize << 1 | 1)
183     @stream << str
184   end
185 end
amf3_write_date(value) click to toggle source
    # File lib/rocketamf/pure/serializer.rb
158 def amf3_write_date(value)
159   @stream << AMF3_DATE_MARKER
160 
161   if @object_cache[value] != nil
162     amf3_write_reference(@object_cache[value])
163   else
164     # Cache date
165     @object_cache.add_obj(value)
166 
167     # Build AMF string
168     @stream << AMF3_NULL_MARKER
169     @stream << pack_double(value.strftime('%Q').to_i)
170   end
171 end
amf3_write_false() click to toggle source
    # File lib/rocketamf/pure/serializer.rb
118 def amf3_write_false
119   @stream << AMF3_FALSE_MARKER
120 end
amf3_write_null() click to toggle source
    # File lib/rocketamf/pure/serializer.rb
108 def amf3_write_null
109   @stream << AMF3_NULL_MARKER
110 end
amf3_write_numeric(value) click to toggle source
    # File lib/rocketamf/pure/serializer.rb
123 def amf3_write_numeric(value)
124   if !value.integer? || value < MIN_INTEGER || value > MAX_INTEGER # Check valid range for 29 bits
125     @stream << AMF3_DOUBLE_MARKER
126     @stream << pack_double(value)
127   else
128     @stream << AMF3_INTEGER_MARKER
129     @stream << pack_integer(value)
130   end
131 end
amf3_write_object(obj, properties = nil, traits = nil) click to toggle source
    # File lib/rocketamf/pure/serializer.rb
235 def amf3_write_object(obj, properties = nil, traits = nil)
236   @stream << AMF3_OBJECT_MARKER
237 
238   # Caching...
239   if @object_cache[obj] != nil
240     amf3_write_reference(@object_cache[obj])
241     return
242   end
243 
244   @object_cache.add_obj(obj)
245 
246   # Calculate traits if not given
247   is_default = false
248   if traits.nil?
249     traits     =
250         {
251             class_name:     @class_mapper.get_as_class_name(obj),
252             members:        [],
253             externalizable: false,
254             dynamic:        true
255         }
256     is_default = true unless traits[:class_name]
257   end
258 
259   class_name = is_default ? '__default__' : traits[:class_name]
260 
261   # Write out traits
262   if class_name && @trait_cache[class_name] != nil
263     @stream << pack_integer(@trait_cache[class_name] << 2 | 0x01)
264   else
265     @trait_cache.add_obj(class_name) if class_name
266 
267     # Write out trait header
268     header = 0x03 # Not object ref and not trait ref
269     header |= 0x02 << 2 if traits[:dynamic]
270     header |= 0x01 << 2 if traits[:externalizable]
271     header |= traits[:members].length << 4
272     @stream << pack_integer(header)
273 
274     # Write out class name
275     if class_name == '__default__'
276       amf3_write_utf8_vr('')
277     else
278       amf3_write_utf8_vr(class_name.to_s)
279     end
280 
281     # Write out members
282     traits[:members].each { |m| amf3_write_utf8_vr(m) }
283   end
284 
285   # If externalizable, take externalized data shortcut
286   if traits[:externalizable]
287     obj.write_external(self)
288     return
289   end
290 
291   # Extract properties if not given
292   properties = @class_mapper.props_for_serialization(obj) if properties.nil?
293 
294   # Write out sealed properties
295   traits[:members].each do |m|
296     amf3_serialize(properties[m])
297     properties.delete(m)
298   end
299 
300   # Write out dynamic properties
301   if traits[:dynamic]
302     # Write out dynamic properties
303     properties.sort.each do |key, val| # Sort props until Ruby 1.9 becomes common
304       amf3_write_utf8_vr(key.to_s)
305       amf3_serialize(val)
306     end
307 
308     # Write close
309     @stream << AMF3_CLOSE_DYNAMIC_OBJECT
310   end
311 end
amf3_write_reference(index) click to toggle source
    # File lib/rocketamf/pure/serializer.rb
102 def amf3_write_reference(index)
103   header = index << 1 # shift value left to leave a low bit of 0
104   @stream << pack_integer(header)
105 end
amf3_write_string(value) click to toggle source
    # File lib/rocketamf/pure/serializer.rb
134 def amf3_write_string(value)
135   @stream << AMF3_STRING_MARKER
136   amf3_write_utf8_vr value
137 end
amf3_write_time(value) click to toggle source
    # File lib/rocketamf/pure/serializer.rb
140 def amf3_write_time(value)
141   @stream << AMF3_DATE_MARKER
142 
143   if @object_cache[value] != nil
144     amf3_write_reference(@object_cache[value])
145   else
146     # Cache time
147     @object_cache.add_obj(value)
148 
149     # Build AMF string
150     value = value.getutc # Dup and convert to UTC
151     milli = (value.to_f * 1000).to_i
152     @stream << AMF3_NULL_MARKER
153     @stream << pack_double(milli)
154   end
155 end
amf3_write_true() click to toggle source
    # File lib/rocketamf/pure/serializer.rb
113 def amf3_write_true
114   @stream << AMF3_TRUE_MARKER
115 end
amf3_write_utf8_vr(value, encode = true) click to toggle source
    # File lib/rocketamf/pure/serializer.rb
314 def amf3_write_utf8_vr(value, encode = true)
315   if value.respond_to?(:encode)
316     if encode
317       value = value.encode('UTF-8')
318     else
319       value = value.dup if value.frozen?
320     end
321     value.force_encoding('ASCII-8BIT')
322   end
323 
324   if value == ''
325     @stream << AMF3_EMPTY_STRING
326   elsif @string_cache[value] != nil
327     amf3_write_reference(@string_cache[value])
328   else
329     # Cache string
330     @string_cache.add_obj(value)
331 
332     # Build AMF string
333     @stream << pack_integer(value.bytesize << 1 | 1)
334     @stream << value
335   end
336 end