class RubySol::Pure::Deserializer
Pure
ruby deserializer for AMF0 and AMF3
Attributes
source[RW]
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/ruby_sol/pure/deserializer.rb 12 def initialize class_mapper 13 @class_mapper = class_mapper 14 reset_caches() 15 end
Public Instance Methods
amf0_deserialize(type=nil)
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 61 def amf0_deserialize type=nil 62 type = read_int8 @source unless type 63 case type 64 when AMF0_NUMBER_MARKER 65 amf0_read_number 66 when AMF0_BOOLEAN_MARKER 67 amf0_read_boolean 68 when AMF0_STRING_MARKER 69 amf0_read_string 70 when AMF0_OBJECT_MARKER 71 amf0_read_object 72 when AMF0_NULL_MARKER 73 nil 74 when AMF0_UNDEFINED_MARKER 75 nil 76 when AMF0_REFERENCE_MARKER 77 amf0_read_reference 78 when AMF0_HASH_MARKER 79 amf0_read_hash 80 when AMF0_STRICT_ARRAY_MARKER 81 amf0_read_array 82 when AMF0_DATE_MARKER 83 amf0_read_date 84 when AMF0_LONG_STRING_MARKER 85 amf0_read_string true 86 when AMF0_UNSUPPORTED_MARKER 87 nil 88 when AMF0_XML_MARKER 89 amf0_read_string true 90 when AMF0_TYPED_OBJECT_MARKER 91 amf0_read_typed_object 92 when AMF0_AMF3_MARKER 93 deserialize(3, nil) 94 else 95 raise AMFError, "Invalid type: #{type}" 96 end 97 end
amf0_read_array()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 120 def amf0_read_array 121 len = read_word32_network(@source) 122 array = [] 123 @ref_cache << array 124 125 0.upto(len - 1) do 126 array << amf0_deserialize 127 end 128 array 129 end
amf0_read_boolean()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 104 def amf0_read_boolean 105 read_int8(@source) != 0 106 end
amf0_read_date()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 131 def amf0_read_date 132 seconds = read_double(@source).to_f/1000 133 time = Time.at(seconds) 134 tz = read_word16_network(@source) # Unused 135 time 136 end
amf0_read_hash()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 148 def amf0_read_hash 149 len = read_word32_network(@source) # Read and ignore length 150 obj = {} 151 @ref_cache << obj 152 amf0_read_props obj 153 end
amf0_read_number()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 99 def amf0_read_number 100 res = read_double @source 101 (res.is_a?(Float) && res.nan?) ? nil : res # check for NaN and convert them to nil 102 end
amf0_read_object(add_to_ref_cache=true)
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 155 def amf0_read_object add_to_ref_cache=true 156 # Create "object" and add to ref cache (it's always a Hash) 157 obj = @class_mapper.get_ruby_obj "" 158 @ref_cache << obj 159 160 # Populate object 161 props = amf0_read_props 162 @class_mapper.populate_ruby_obj obj, props 163 return obj 164 end
amf0_read_props(obj={})
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 138 def amf0_read_props obj={} 139 while true 140 key = amf0_read_string 141 type = read_int8 @source 142 break if type == AMF0_OBJECT_END_MARKER 143 obj[key] = amf0_deserialize(type) 144 end 145 obj 146 end
amf0_read_reference()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 115 def amf0_read_reference 116 index = read_word16_network(@source) 117 @ref_cache[index] 118 end
amf0_read_string(long=false)
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 108 def amf0_read_string long=false 109 len = long ? read_word32_network(@source) : read_word16_network(@source) 110 str = @source.read(len) 111 str.force_encoding("UTF-8") if str.respond_to?(:force_encoding) 112 str 113 end
amf0_read_typed_object()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 166 def amf0_read_typed_object 167 # Create object to add to ref cache 168 class_name = amf0_read_string 169 obj = @class_mapper.get_ruby_obj class_name 170 @ref_cache << obj 171 172 # Populate object 173 props = amf0_read_props 174 @class_mapper.populate_ruby_obj obj, props 175 return obj 176 end
amf3_deserialize()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 178 def amf3_deserialize 179 type = read_int8 @source 180 case type 181 when AMF3_UNDEFINED_MARKER 182 nil 183 when AMF3_NULL_MARKER 184 nil 185 when AMF3_FALSE_MARKER 186 false 187 when AMF3_TRUE_MARKER 188 true 189 when AMF3_INTEGER_MARKER 190 amf3_read_integer 191 when AMF3_DOUBLE_MARKER 192 amf3_read_number 193 when AMF3_STRING_MARKER 194 amf3_read_string 195 when AMF3_XML_DOC_MARKER, AMF3_XML_MARKER 196 amf3_read_xml 197 when AMF3_DATE_MARKER 198 amf3_read_date 199 when AMF3_ARRAY_MARKER 200 amf3_read_array 201 when AMF3_OBJECT_MARKER 202 amf3_read_object 203 when AMF3_BYTE_ARRAY_MARKER 204 amf3_read_byte_array 205 when AMF3_VECTOR_INT_MARKER, AMF3_VECTOR_UINT_MARKER, AMF3_VECTOR_DOUBLE_MARKER, AMF3_VECTOR_OBJECT_MARKER 206 amf3_read_vector type 207 when AMF3_DICT_MARKER 208 amf3_read_dict 209 else 210 raise AMFError, "Invalid type: #{type}" 211 end 212 end
amf3_read_array()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 300 def amf3_read_array 301 type = amf3_read_integer 302 is_reference = (type & 0x01) == 0 303 304 if is_reference 305 reference = type >> 1 306 return @object_cache[reference] 307 else 308 length = type >> 1 309 property_name = amf3_read_string 310 array = property_name.length > 0 ? {} : [] 311 @object_cache << array 312 313 while property_name.length > 0 314 value = amf3_deserialize 315 array[property_name] = value 316 property_name = amf3_read_string 317 end 318 0.upto(length - 1) {|i| array[i] = amf3_deserialize } 319 320 array 321 end 322 end
amf3_read_byte_array()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 285 def amf3_read_byte_array 286 type = amf3_read_integer 287 is_reference = (type & 0x01) == 0 288 289 if is_reference 290 reference = type >> 1 291 return @object_cache[reference] 292 else 293 length = type >> 1 294 obj = StringIO.new @source.read(length) 295 @object_cache << obj 296 obj 297 end 298 end
amf3_read_date()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 390 def amf3_read_date 391 type = amf3_read_integer 392 is_reference = (type & 0x01) == 0 393 if is_reference 394 reference = type >> 1 395 return @object_cache[reference] 396 else 397 seconds = read_double(@source).to_f/1000 398 time = Time.at(seconds) 399 @object_cache << time 400 time 401 end 402 end
amf3_read_dict()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 404 def amf3_read_dict 405 type = amf3_read_integer 406 is_reference = (type & 0x01) == 0 407 if is_reference 408 reference = type >> 1 409 return @object_cache[reference] 410 else 411 dict = {} 412 @object_cache << dict 413 length = type >> 1 414 weak_keys = read_int8 @source # Ignore: Not supported in ruby 415 0.upto(length - 1) do |i| 416 dict[amf3_deserialize] = amf3_deserialize 417 end 418 dict 419 end 420 end
amf3_read_integer()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 214 def amf3_read_integer 215 n = 0 216 b = read_word8(@source) || 0 217 result = 0 218 219 while ((b & 0x80) != 0 && n < 3) 220 result = result << 7 221 result = result | (b & 0x7f) 222 b = read_word8(@source) || 0 223 n = n + 1 224 end 225 226 if (n < 3) 227 result = result << 7 228 result = result | b 229 else 230 #Use all 8 bits from the 4th byte 231 result = result << 8 232 result = result | b 233 234 #Check if the integer should be negative 235 if (result > MAX_INTEGER) 236 result -= (1 << 29) 237 end 238 end 239 result 240 end
amf3_read_number()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 242 def amf3_read_number 243 res = read_double @source 244 (res.is_a?(Float) && res.nan?) ? nil : res # check for NaN and convert them to nil 245 end
amf3_read_object()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 324 def amf3_read_object 325 type = amf3_read_integer 326 is_reference = (type & 0x01) == 0 327 328 if is_reference 329 reference = type >> 1 330 return @object_cache[reference] 331 else 332 class_type = type >> 1 333 class_is_reference = (class_type & 0x01) == 0 334 335 if class_is_reference 336 reference = class_type >> 1 337 traits = @trait_cache[reference] 338 else 339 externalizable = (class_type & 0x02) != 0 340 dynamic = (class_type & 0x04) != 0 341 attribute_count = class_type >> 3 342 class_name = amf3_read_string 343 344 class_attributes = [] 345 attribute_count.times{class_attributes << amf3_read_string} # Read class members 346 347 traits = { 348 :class_name => class_name, 349 :members => class_attributes, 350 :externalizable => externalizable, 351 :dynamic => dynamic 352 } 353 @trait_cache << traits 354 end 355 356 # Optimization for deserializing ArrayCollection 357 if traits[:class_name] == "flex.messaging.io.ArrayCollection" 358 arr = amf3_deserialize # Adds ArrayCollection array to object cache 359 @object_cache << arr # Add again for ArrayCollection source array 360 return arr 361 end 362 363 obj = @class_mapper.get_ruby_obj traits[:class_name] 364 @object_cache << obj 365 366 if traits[:externalizable] 367 obj.read_external self 368 else 369 props = {} 370 traits[:members].each do |key| 371 value = amf3_deserialize 372 props[key] = value 373 end 374 375 dynamic_props = nil 376 if traits[:dynamic] 377 dynamic_props = {} 378 while (key = amf3_read_string) && key.length != 0 do # read next key 379 value = amf3_deserialize 380 dynamic_props[key] = value 381 end 382 end 383 384 @class_mapper.populate_ruby_obj obj, props, dynamic_props 385 end 386 obj 387 end 388 end
amf3_read_string()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 247 def amf3_read_string 248 type = amf3_read_integer 249 is_reference = (type & 0x01) == 0 250 251 if is_reference 252 reference = type >> 1 253 return @string_cache[reference] 254 else 255 length = type >> 1 256 str = "" 257 if length > 0 258 str = @source.read(length) 259 str.force_encoding("UTF-8") if str.respond_to?(:force_encoding) 260 @string_cache << str 261 end 262 return str 263 end 264 end
amf3_read_vector(vector_type)
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 422 def amf3_read_vector vector_type 423 type = amf3_read_integer 424 is_reference = (type & 0x01) == 0 425 if is_reference 426 reference = type >> 1 427 return @object_cache[reference] 428 else 429 vec = [] 430 @object_cache << vec 431 length = type >> 1 432 fixed_vector = read_int8 @source # Ignore 433 case vector_type 434 when AMF3_VECTOR_INT_MARKER 435 0.upto(length - 1) do |i| 436 int = read_word32_network(@source) 437 int = int - 2**32 if int > MAX_INTEGER 438 vec << int 439 end 440 when AMF3_VECTOR_UINT_MARKER 441 0.upto(length - 1) do |i| 442 vec << read_word32_network(@source) 443 # puts vec[i].to_s(2) 444 end 445 when AMF3_VECTOR_DOUBLE_MARKER 446 0.upto(length - 1) do |i| 447 vec << amf3_read_number 448 end 449 when AMF3_VECTOR_OBJECT_MARKER 450 vector_class = amf3_read_string # Ignore 451 # puts vector_class 452 0.upto(length - 1) do |i| 453 vec << amf3_deserialize 454 end 455 end 456 vec 457 end 458 end
amf3_read_xml()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 266 def amf3_read_xml 267 type = amf3_read_integer 268 is_reference = (type & 0x01) == 0 269 270 if is_reference 271 reference = type >> 1 272 return @object_cache[reference] 273 else 274 length = type >> 1 275 str = "" 276 if length > 0 277 str = @source.read(length) 278 str.force_encoding("UTF-8") if str.respond_to?(:force_encoding) 279 @object_cache << str 280 end 281 return str 282 end 283 end
deserialize(version, source)
click to toggle source
Deserialize the source using AMF0 or 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.
# File lib/ruby_sol/pure/deserializer.rb 28 def deserialize version, source 29 raise ArgumentError, "unsupported version #{version}" unless [0,3].include?(version) 30 @version = version 31 32 if StringIO === source 33 @source = source 34 elsif source 35 @source = StringIO.new(source) 36 elsif @source.nil? 37 raise AMFError, "no source to deserialize" 38 end 39 40 reset_caches() 41 42 if @version == 0 43 return amf0_deserialize 44 else 45 return amf3_deserialize 46 end 47 end
read_object()
click to toggle source
Reads an object from the deserializer’s stream and returns it.
# File lib/ruby_sol/pure/deserializer.rb 50 def read_object 51 if @version == 0 52 return amf0_deserialize 53 else 54 return amf3_deserialize 55 end 56 end
reset_caches()
click to toggle source
# File lib/ruby_sol/pure/deserializer.rb 17 def reset_caches 18 @ref_cache = [] 19 @string_cache = [] 20 @object_cache = [] 21 @trait_cache = [] 22 end