lib/rocketamf/pure/serializer.rb in mrpin-rocketamf-1.0.4 vs lib/rocketamf/pure/serializer.rb in mrpin-rocketamf-2.0.0
- old
+ new
@@ -1,368 +1,270 @@
-require 'rocketamf/pure/io_helpers'
+require 'rocketamf/pure/helpers/io_helper_write'
+require 'rocketamf/pure/helpers/object_cache'
+require 'rocketamf/pure/helpers/string_cache'
module RocketAMF
module Pure
- # Pure ruby serializer for AMF0 and AMF3
+ # Pure ruby serializer for AMF3
class Serializer
- attr_reader :stream, :version
+ #
+ # Modules
+ #
+
+ private
+ include RocketAMF::Pure::IOHelperWrite
+
+ #
+ # Properties
+ #
+
+ public
+ attr_reader :stream
+
# 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.
- def initialize class_mapper
+ public
+ def initialize(class_mapper)
@class_mapper = class_mapper
- @stream = ""
- @depth = 0
+ @stream = ''
+ @depth = 0
end
- # Serialize the given object using AMF0 or AMF3. Can be called from inside
- # encode_amf, but make sure to pass in the proper version or it may not be
- # possible to decode. Use the serializer version attribute for this.
- def serialize version, obj
- raise ArgumentError, "unsupported version #{version}" unless [0,3].include?(version)
- @version = version
-
+ # Serialize the given object using AMF3. Can be called from inside
+ # encode_amf.
+ def serialize(obj)
# Initialize caches
if @depth == 0
- if @version == 0
- @ref_cache = SerializerCache.new :object
- else
- @string_cache = SerializerCache.new :string
- @object_cache = SerializerCache.new :object
- @trait_cache = SerializerCache.new :string
- end
+ @string_cache = StringCache.new
+ @object_cache = ObjectCache.new
+ @trait_cache = StringCache.new
end
@depth += 1
# Perform serialization
- if @version == 0
- amf0_serialize(obj)
- else
- amf3_serialize(obj)
- end
+ amf3_serialize(obj)
# Cleanup
@depth -= 1
if @depth == 0
- @ref_cache = nil
+ @ref_cache = nil
@string_cache = nil
@object_cache = nil
- @trait_cache = nil
+ @trait_cache = nil
end
- return @stream
+ @stream
end
- # Helper for writing arrays inside encode_amf. It uses the current AMF
- # version to write the array.
- def write_array arr
- if @version == 0
- amf0_write_array arr
- else
- amf3_write_array arr
- end
+ # Helper for writing arrays inside encode_amf.
+ def write_array(value)
+ amf3_write_array value
end
- # Helper for writing objects inside encode_amf. It uses the current AMF
- # version to write the object. If you pass in a property hash, it will use
- # it rather than having the class mapper determine properties. For AMF3,
- # you can also specify a traits hash, which can be used to reduce serialized
+ # 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.
- def write_object obj, props=nil, traits=nil
- if @version == 0
- amf0_write_object obj, props
- else
- amf3_write_object obj, props, traits
- end
+ def write_object(obj, props = nil, traits = nil)
+ amf3_write_object(obj, props, traits)
end
- private
- include RocketAMF::Pure::WriteIOHelpers
- def amf0_serialize obj
- if @ref_cache[obj] != nil
- amf0_write_reference @ref_cache[obj]
- elsif obj.respond_to?(:encode_amf)
- obj.encode_amf(self)
- elsif obj.is_a?(NilClass)
- amf0_write_null
- elsif obj.is_a?(TrueClass) || obj.is_a?(FalseClass)
- amf0_write_boolean obj
- elsif obj.is_a?(Numeric)
- amf0_write_number obj
- elsif obj.is_a?(Symbol) || obj.is_a?(String)
- amf0_write_string obj.to_s
- elsif obj.is_a?(Time)
- amf0_write_time obj
- elsif obj.is_a?(Date)
- amf0_write_date obj
- elsif obj.is_a?(Array)
- amf0_write_array obj
- elsif obj.is_a?(Hash) ||obj.is_a?(Object)
- amf0_write_object obj
- end
- end
-
- def amf0_write_null
- @stream << AMF0_NULL_MARKER
- end
-
- def amf0_write_boolean bool
- @stream << AMF0_BOOLEAN_MARKER
- @stream << pack_int8(bool ? 1 : 0)
- end
-
- def amf0_write_number num
- @stream << AMF0_NUMBER_MARKER
- @stream << pack_double(num)
- end
-
- def amf0_write_string str
- str = str.encode("UTF-8").force_encoding("ASCII-8BIT") if str.respond_to?(:encode)
- len = str.bytesize
- if len > 2**16-1
- @stream << AMF0_LONG_STRING_MARKER
- @stream << pack_word32_network(len)
- else
- @stream << AMF0_STRING_MARKER
- @stream << pack_int16_network(len)
- end
- @stream << str
- end
-
- def amf0_write_time time
- @stream << AMF0_DATE_MARKER
-
- time = time.getutc # Dup and convert to UTC
- milli = (time.to_f * 1000).to_i
- @stream << pack_double(milli)
-
- @stream << pack_int16_network(0) # Time zone
- end
-
- def amf0_write_date date
- @stream << AMF0_DATE_MARKER
- @stream << pack_double(date.strftime("%Q").to_i)
- @stream << pack_int16_network(0) # Time zone
- end
-
- def amf0_write_reference index
- @stream << AMF0_REFERENCE_MARKER
- @stream << pack_int16_network(index)
- end
-
- def amf0_write_array array
- @ref_cache.add_obj array
- @stream << AMF0_STRICT_ARRAY_MARKER
- @stream << pack_word32_network(array.length)
- array.each do |elem|
- amf0_serialize elem
- end
- end
-
- def amf0_write_object obj, props=nil
- @ref_cache.add_obj obj
-
- props = @class_mapper.props_for_serialization obj if props.nil?
-
- # Is it a typed object?
- class_name = @class_mapper.get_as_class_name obj
- if class_name
- class_name = class_name.encode("UTF-8").force_encoding("ASCII-8BIT") if class_name.respond_to?(:encode)
- @stream << AMF0_TYPED_OBJECT_MARKER
- @stream << pack_int16_network(class_name.bytesize)
- @stream << class_name
- else
- @stream << AMF0_OBJECT_MARKER
- end
-
- # Write prop list
- props.sort.each do |key, value| # Sort keys before writing
- key = key.encode("UTF-8").force_encoding("ASCII-8BIT") if key.respond_to?(:encode)
- @stream << pack_int16_network(key.bytesize)
- @stream << key
- amf0_serialize value
- end
-
- # Write end
- @stream << pack_int16_network(0)
- @stream << AMF0_OBJECT_END_MARKER
- end
-
- def amf3_serialize obj
+ private
+ def amf3_serialize(obj)
if obj.respond_to?(:encode_amf)
obj.encode_amf(self)
elsif obj.is_a?(NilClass)
amf3_write_null
elsif obj.is_a?(TrueClass)
amf3_write_true
elsif obj.is_a?(FalseClass)
amf3_write_false
elsif obj.is_a?(Numeric)
- amf3_write_numeric obj
+ amf3_write_numeric(obj)
elsif obj.is_a?(Symbol) || obj.is_a?(String)
- amf3_write_string obj.to_s
+ amf3_write_string(obj.to_s)
elsif obj.is_a?(Time)
amf3_write_time obj
elsif obj.is_a?(Date)
- amf3_write_date obj
+ amf3_write_date(obj)
elsif obj.is_a?(StringIO)
- amf3_write_byte_array obj
+ amf3_write_byte_array(obj)
elsif obj.is_a?(Array)
- amf3_write_array obj
+ amf3_write_array(obj)
elsif obj.is_a?(Hash) || obj.is_a?(Object)
- amf3_write_object obj
+ amf3_write_object(obj)
end
end
- def amf3_write_reference index
+ private
+ def amf3_write_reference(index)
header = index << 1 # shift value left to leave a low bit of 0
@stream << pack_integer(header)
end
+ private
def amf3_write_null
@stream << AMF3_NULL_MARKER
end
+ private
def amf3_write_true
@stream << AMF3_TRUE_MARKER
end
+ private
def amf3_write_false
@stream << AMF3_FALSE_MARKER
end
- def amf3_write_numeric num
- if !num.integer? || num < MIN_INTEGER || num > MAX_INTEGER # Check valid range for 29 bits
+ private
+ def amf3_write_numeric(value)
+ if !value.integer? || value < MIN_INTEGER || value > MAX_INTEGER # Check valid range for 29 bits
@stream << AMF3_DOUBLE_MARKER
- @stream << pack_double(num)
+ @stream << pack_double(value)
else
@stream << AMF3_INTEGER_MARKER
- @stream << pack_integer(num)
+ @stream << pack_integer(value)
end
end
- def amf3_write_string str
+ private
+ def amf3_write_string(value)
@stream << AMF3_STRING_MARKER
- amf3_write_utf8_vr str
+ amf3_write_utf8_vr value
end
- def amf3_write_time time
+ private
+ def amf3_write_time(value)
@stream << AMF3_DATE_MARKER
- if @object_cache[time] != nil
- amf3_write_reference @object_cache[time]
+
+ if @object_cache[value] != nil
+ amf3_write_reference(@object_cache[value])
else
# Cache time
- @object_cache.add_obj time
+ @object_cache.add_obj(value)
# Build AMF string
- time = time.getutc # Dup and convert to UTC
- milli = (time.to_f * 1000).to_i
+ value = value.getutc # Dup and convert to UTC
+ milli = (value.to_f * 1000).to_i
@stream << AMF3_NULL_MARKER
@stream << pack_double(milli)
end
end
- def amf3_write_date date
+ private
+ def amf3_write_date(value)
@stream << AMF3_DATE_MARKER
- if @object_cache[date] != nil
- amf3_write_reference @object_cache[date]
+
+ if @object_cache[value] != nil
+ amf3_write_reference(@object_cache[value])
else
# Cache date
- @object_cache.add_obj date
+ @object_cache.add_obj(value)
# Build AMF string
@stream << AMF3_NULL_MARKER
- @stream << pack_double(date.strftime("%Q").to_i)
+ @stream << pack_double(value.strftime('%Q').to_i)
end
end
- def amf3_write_byte_array array
+ private
+ def amf3_write_byte_array(value)
@stream << AMF3_BYTE_ARRAY_MARKER
- if @object_cache[array] != nil
- amf3_write_reference @object_cache[array]
+
+ if @object_cache[value] != nil
+ amf3_write_reference(@object_cache[value])
else
- @object_cache.add_obj array
- str = array.string
+ @object_cache.add_obj(value)
+ str = value.string
@stream << pack_integer(str.bytesize << 1 | 1)
@stream << str
end
end
- def amf3_write_array array
+ private
+ def amf3_write_array(value)
# Is it an array collection?
- is_ac = false
- if array.respond_to?(:is_array_collection?)
- is_ac = array.is_array_collection?
+ is_array_collection = false
+
+ if value.respond_to?(:is_array_collection?)
+ is_array_collection = value.is_array_collection?
else
- is_ac = @class_mapper.use_array_collection
+ is_array_collection = @class_mapper.use_array_collection
end
# Write type marker
- @stream << (is_ac ? AMF3_OBJECT_MARKER : AMF3_ARRAY_MARKER)
+ @stream << (is_array_collection ? AMF3_OBJECT_MARKER : AMF3_ARRAY_MARKER)
# Write reference or cache array
- if @object_cache[array] != nil
- amf3_write_reference @object_cache[array]
+ if @object_cache[value] != nil
+ amf3_write_reference(@object_cache[value])
return
else
- @object_cache.add_obj array
- @object_cache.add_obj nil if is_ac # The array collection source array
+ @object_cache.add_obj(value)
+ @object_cache.add_obj(nil) if is_array_collection # The array collection source array
end
# Write out traits and array marker if it's an array collection
- if is_ac
- class_name = "flex.messaging.io.ArrayCollection"
+ if is_array_collection
+ class_name = 'flex.messaging.io.ArrayCollection'
+
if @trait_cache[class_name] != nil
@stream << pack_integer(@trait_cache[class_name] << 2 | 0x01)
else
- @trait_cache.add_obj class_name
+ @trait_cache.add_obj(class_name)
@stream << "\a" # Externalizable, non-dynamic
amf3_write_utf8_vr(class_name)
end
@stream << AMF3_ARRAY_MARKER
end
# Build AMF string for array
- header = array.length << 1 # make room for a low bit of 1
+ header = value.length << 1 # make room for a low bit of 1
header = header | 1 # set the low bit to 1
@stream << pack_integer(header)
@stream << AMF3_CLOSE_DYNAMIC_ARRAY
- array.each do |elem|
+ value.each do |elem|
amf3_serialize elem
end
end
- def amf3_write_object obj, props=nil, traits=nil
+ private
+ def amf3_write_object(obj, properties = nil, traits = nil)
@stream << AMF3_OBJECT_MARKER
# Caching...
if @object_cache[obj] != nil
- amf3_write_reference @object_cache[obj]
+ amf3_write_reference(@object_cache[obj])
return
end
- @object_cache.add_obj obj
+ @object_cache.add_obj(obj)
+
# Calculate traits if not given
is_default = false
if traits.nil?
- traits = {
- :class_name => @class_mapper.get_as_class_name(obj),
- :members => [],
- :externalizable => false,
- :dynamic => true
- }
+ traits =
+ {
+ class_name: @class_mapper.get_as_class_name(obj),
+ members: [],
+ externalizable: false,
+ dynamic: true
+ }
is_default = true unless traits[:class_name]
end
- class_name = is_default ? "__default__" : traits[:class_name]
+ class_name = is_default ? '__default__' : traits[:class_name]
+
# Write out traits
if class_name && @trait_cache[class_name] != nil
@stream << pack_integer(@trait_cache[class_name] << 2 | 0x01)
else
- @trait_cache.add_obj class_name if class_name
+ @trait_cache.add_obj(class_name) if class_name
# Write out trait header
header = 0x03 # Not object ref and not trait ref
header |= 0x02 << 2 if traits[:dynamic]
header |= 0x01 << 2 if traits[:externalizable]
@@ -375,100 +277,65 @@
else
amf3_write_utf8_vr(class_name.to_s)
end
# Write out members
- traits[:members].each {|m| amf3_write_utf8_vr(m)}
+ traits[:members].each { |m| amf3_write_utf8_vr(m) }
end
# If externalizable, take externalized data shortcut
if traits[:externalizable]
obj.write_external(self)
return
end
# Extract properties if not given
- props = @class_mapper.props_for_serialization(obj) if props.nil?
+ properties = @class_mapper.props_for_serialization(obj) if properties.nil?
# Write out sealed properties
traits[:members].each do |m|
- amf3_serialize props[m]
- props.delete(m)
+ amf3_serialize(properties[m])
+ properties.delete(m)
end
# Write out dynamic properties
if traits[:dynamic]
# Write out dynamic properties
- props.sort.each do |key, val| # Sort props until Ruby 1.9 becomes common
- amf3_write_utf8_vr key.to_s
- amf3_serialize val
+ properties.sort.each do |key, val| # Sort props until Ruby 1.9 becomes common
+ amf3_write_utf8_vr(key.to_s)
+ amf3_serialize(val)
end
# Write close
@stream << AMF3_CLOSE_DYNAMIC_OBJECT
end
end
- def amf3_write_utf8_vr str, encode=true
- if str.respond_to?(:encode)
+ private
+ def amf3_write_utf8_vr(value, encode = true)
+ if value.respond_to?(:encode)
if encode
- str = str.encode("UTF-8")
+ value = value.encode('UTF-8')
else
- str = str.dup if str.frozen?
+ value = value.dup if value.frozen?
end
- str.force_encoding("ASCII-8BIT")
+ value.force_encoding('ASCII-8BIT')
end
- if str == ''
+ if value == ''
@stream << AMF3_EMPTY_STRING
- elsif @string_cache[str] != nil
- amf3_write_reference @string_cache[str]
+ elsif @string_cache[value] != nil
+ amf3_write_reference(@string_cache[value])
else
# Cache string
- @string_cache.add_obj str
+ @string_cache.add_obj(value)
# Build AMF string
- @stream << pack_integer(str.bytesize << 1 | 1)
- @stream << str
+ @stream << pack_integer(value.bytesize << 1 | 1)
+ @stream << value
end
end
- end
- class SerializerCache #:nodoc:
- def self.new type
- if type == :string
- StringCache.new
- elsif type == :object
- ObjectCache.new
- end
- end
+ end # Serializer
- class StringCache < Hash #:nodoc:
- def initialize
- @cache_index = 0
- end
-
- def add_obj str
- self[str] = @cache_index
- @cache_index += 1
- end
- end
-
- class ObjectCache < Hash #:nodoc:
- def initialize
- @cache_index = 0
- @obj_references = []
- end
-
- def [] obj
- super(obj.object_id)
- end
-
- def add_obj obj
- @obj_references << obj
- self[obj.object_id] = @cache_index
- @cache_index += 1
- end
- end
- end
- end
-end
+ end #Pure
+end #RocketAMF