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