lib/amf/pure/serializer.rb in rack-amf-0.0.2 vs lib/amf/pure/serializer.rb in rack-amf-0.0.3
- old
+ new
@@ -3,31 +3,139 @@
module AMF
module Pure
# AMF0 implementation of serializer
class Serializer
def initialize
- @ref_cache = SerializerCache.new
+ @ref_cache = SerializerCache.new :object
end
def version
0
end
def serialize obj, stream = ""
- if @ref_cache[obj] != nil
- # Write reference header
+ if obj.respond_to?(:to_amf)
+ stream << obj.to_amf(self)
+ elsif @ref_cache[obj] != nil
+ write_reference @ref_cache[obj], stream
+ elsif obj.is_a?(NilClass)
+ write_null stream
+ elsif obj.is_a?(TrueClass) || obj.is_a?(FalseClass)
+ write_boolean obj, stream
+ elsif obj.is_a?(Float) || obj.is_a?(Integer)
+ write_number obj, stream
+ elsif obj.is_a?(Symbol) || obj.is_a?(String)
+ write_string obj.to_s, stream
+ elsif obj.is_a?(Time)
+ write_date obj, stream
+ elsif obj.is_a?(Array)
+ write_array obj, stream
+ elsif obj.is_a?(Hash)
+ write_hash obj, stream
+ elsif obj.is_a?(Object)
+ write_object obj, stream
end
+ stream
end
+
+ def write_null stream
+ stream << AMF0_NULL_MARKER
+ end
+
+ def write_boolean bool, stream
+ stream << AMF0_BOOLEAN_MARKER
+ stream << pack_int8(bool ? 1 : 0)
+ end
+
+ def write_number num, stream
+ stream << AMF0_NUMBER_MARKER
+ stream << pack_double(num)
+ end
+
+ def write_string str, stream
+ len = str.length
+ 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 write_date date, stream
+ stream << AMF0_DATE_MARKER
+
+ date.utc unless date.utc?
+ seconds = (date.to_f * 1000).to_i
+ stream << pack_double(seconds)
+
+ stream << pack_int16_network(0)
+ end
+
+ def write_reference index, stream
+ stream << AMF0_REFERENCE_MARKER
+ stream << pack_int16_network(index)
+ end
+
+ def write_array array, stream
+ @ref_cache.add_obj array
+ stream << AMF0_STRICT_ARRAY_MARKER
+ stream << pack_word32_network(array.length)
+ array.each do |elem|
+ serialize elem, stream
+ end
+ end
+
+ def write_hash hash, stream
+ @ref_cache.add_obj hash
+ stream << AMF0_HASH_MARKER
+ stream << pack_word32_network(hash.length)
+ write_prop_list hash, stream
+ end
+
+ def write_object obj, stream
+ @ref_cache.add_obj obj
+
+ # Is it a typed object?
+ class_name = ClassMapper.get_as_class_name obj
+ if class_name
+ stream << AMF0_TYPED_OBJECT_MARKER
+ stream << pack_int16_network(class_name.length)
+ stream << class_name
+ else
+ stream << AMF0_OBJECT_MARKER
+ end
+
+ write_prop_list obj, stream
+ end
+
+ private
+ include AMF::Pure::WriteIOHelpers
+ def write_prop_list obj, stream
+ # Write prop list
+ props = ClassMapper.props_for_serialization obj
+ props.sort.each do |key, value| # Sort keys before writing
+ stream << pack_int16_network(key.length)
+ stream << key
+ serialize value, stream
+ end
+
+ # Write end
+ stream << pack_int16_network(0)
+ stream << AMF0_OBJECT_END_MARKER
+ end
end
# AMF3 implementation of serializer
class AMF3Serializer
attr_reader :string_cache
def initialize
- @string_cache = SerializerCache.new
- @object_cache = SerializerCache.new
+ @string_cache = SerializerCache.new :string
+ @object_cache = SerializerCache.new :object
end
def version
3
end
@@ -119,11 +227,11 @@
# Build AMF string
header = array.length << 1 # make room for a low bit of 1
header = header | 1 # set the low bit to 1
stream << pack_integer(header)
- stream << CLOSE_DYNAMIC_ARRAY
+ stream << AMF3_CLOSE_DYNAMIC_ARRAY
array.each do |elem|
serialize elem, stream
end
end
end
@@ -135,38 +243,38 @@
else
# Cache object
@object_cache.add_obj obj
# Always serialize things as dynamic objects
- stream << DYNAMIC_OBJECT
+ stream << AMF3_DYNAMIC_OBJECT
# Write class name/anonymous
class_name = ClassMapper.get_as_class_name obj
if class_name
write_utf8_vr class_name, stream
else
- stream << ANONYMOUS_OBJECT
+ stream << AMF3_ANONYMOUS_OBJECT
end
# Write out properties
props = ClassMapper.props_for_serialization obj
props.sort.each do |key, val| # Sort props until Ruby 1.9 becomes common
write_utf8_vr key.to_s, stream
serialize val, stream
end
# Write close
- stream << CLOSE_DYNAMIC_OBJECT
+ stream << AMF3_CLOSE_DYNAMIC_OBJECT
end
end
private
include AMF::Pure::WriteIOHelpers
def write_utf8_vr str, stream
if str == ''
- stream << EMPTY_STRING
+ stream << AMF3_EMPTY_STRING
elsif @string_cache[str] != nil
write_reference @string_cache[str], stream
else
# Cache string
@string_cache.add_obj str
@@ -179,36 +287,40 @@
end
end
end
class SerializerCache #:nodoc:
- def initialize
- @cache_index = 0
- @store = {}
+ def self.new type
+ if type == :string
+ StringCache.new
+ elsif type == :object
+ ObjectCache.new
+ end
end
- def [] obj
- @store[object_key(obj)]
- end
+ class StringCache < Hash #:nodoc:
+ def initialize
+ @cache_index = 0
+ end
- def []= obj, value
- @store[object_key(obj)] = value
- end
-
- def add_obj obj
- key = object_key obj
- if @store[key].nil?
- @store[key] = @cache_index
+ def add_obj str
+ self[str] = @cache_index
@cache_index += 1
end
end
- private
- def object_key obj
- if obj.is_a?(String)
- obj
- else
- obj.object_id
+ class ObjectCache < Hash #:nodoc:
+ def initialize
+ @cache_index = 0
+ end
+
+ def [] obj
+ super(obj.object_id)
+ end
+
+ def add_obj obj
+ self[obj.object_id] = @cache_index
+ @cache_index += 1
end
end
end
end
end
\ No newline at end of file