class AMF::Serializer

Attributes

data[RW]

Public Class Methods

new( headers, messages, amfVersion ) click to toggle source
# File lib/amf/serializer.rb, line 6
def initialize( headers, messages, amfVersion )
  @headers      = headers
  @messages     = messages
  @amfVersion   = amfVersion
  @data         = String.new

  writeInt 0 # start if off right

  # start writing the headers
  writeInt @headers.count
  @headers.each do |header|
    resetReferences
    writeUTF header.target
    if header.required == true
      writeByte 1
    else
      writeByte 0
    end
    tmpdata = @data
    @data = String.new
    writeData header.data
    serializedHeader = @data
    @data = tmpdata
    writeLong serializedHeader.length
    @data += serializedHeader
  end

  # and write the data
  writeInt @messages.count
  @messages.each do |message|
    resetReferences
    writeUTF message.targetURL
    writeUTF message.responseURL
    tmpdata = @data
    @data = String.new
    writeData message.data
    serializedMessage = @data
    @data = tmpdata
    writeLong serializedMessage.length
    @data += serializedMessage
  end
end

Private Instance Methods

handleReference( obj, reference ) click to toggle source
# File lib/amf/serializer.rb, line 258
def handleReference( obj, reference )
  key = false
  hash = obj.hash.to_s
  if reference[ hash ].nil?
    if reference.keys.count <= AMF_MAX_STORED_OBJECTS
      reference[ hash ] = reference.keys.count
    end
  else
    key = reference[ hash ]
  end

  if key
    if @amfVersion == AMF0_VERSION
      raise "unsupported AMF0_VERSION reference"
    else
      handle = key << 1
      writeAMF3Int( handle )
      return true
    end
  else
    return false
  end
end
resetReferences() click to toggle source
# File lib/amf/serializer.rb, line 282
def resetReferences
  @storedObjects          = Hash.new
  @storedStrings          = Hash.new
  @className2TraitsInfo   = Hash.new
end
writeAMF3AnonymousObject( data, doRef = true ) click to toggle source
# File lib/amf/serializer.rb, line 231
def writeAMF3AnonymousObject( data, doRef = true )
  writeByte( AMF3_OBJECT_MARKER )
  if doRef && handleReference( data, @storedObjects )
    return
  end

  writeAMF3Int( 0xB )
  @className2TraitsInfo[ data.hash ] = Hash.new
  writeAMF3String( '' )
  data.marshal_dump.each do |key, value|
    writeAMF3String( key.to_s )
    writeAMF3Data( value )
  end
  writeByte( AMF3_NULL_MARKER )
end
writeAMF3Array( array ) click to toggle source
# File lib/amf/serializer.rb, line 176
def writeAMF3Array( array )
  if @storedObjects.keys.count <= AMF_MAX_STORED_OBJECTS
    @storedObjects[ @storedObjects.keys.count ] = @storedObjects.keys.count
  end

  # meh, so there is a whole boat of stuff that we're missing here -
  # arrays that are sparse, arrays with string 'keys'.  in my testing
  # I never saw any of those data types so just doing it the easy way
  writeByte( AMF3_ARRAY_MARKER )
  writeAMF3Int( ( array.count * 2 ) + 1 )
  array.select{ |x| x.class == String }.each do |key, value|
    writeAMF3String( key.to_s )
    writeAMF3Data( value )
  end
  writeAMF3String('')

  array.select{ |x| x.class == Fixnum }.each do |key,value|
    writeAMF3Data value
  end
end
writeAMF3Data( data ) click to toggle source
# File lib/amf/serializer.rb, line 60
def writeAMF3Data( data )
  case data.class.to_s
    when 'Fixnum'
      writeAMF3Number( data )
    when 'Float'
      writeByte( AMF3_DOUBLE_MARKER )
      writeDouble( data )
    when 'String'
      writeByte( AMF3_STRING_MARKER )
      writeAMF3String( data )
    when 'FalseClass'
      writeByte( AMF3_FALSE_MARKER )
    when 'TrueClass'
      writeByte( AMF3_TRUE_MARKER )
    when 'NilClass'
      writeByte( AMF3_NULL_MARKER )
    when 'Time'
      writeAMF3Date( data )
    when 'Hash'
      writeAMF3Array( data )
    when 'OpenStruct'
      if data.send( AMF_FIELD_EXPLICIT_TYPE )
        writeAMF3TypedObject( data )
      else
        writeAMF3AnonymousObject( data )
      end
  else
     raise "Unknown data type: #{ data.class }"
  end
end
writeAMF3Date( date ) click to toggle source

in ruby speak this is time, but we go with the status quo

# File lib/amf/serializer.rb, line 170
def writeAMF3Date( date )
  writeByte AMF3_DATE_MARKER
  writeAMF3Int 1
  writeDouble date.to_f
end
writeAMF3Int( int ) click to toggle source
# File lib/amf/serializer.rb, line 145
def writeAMF3Int( int )
  int &= 0x1fffffff
  if int < 0x80
    data = int.chr
  elsif int < 0x4000
    data = ( int >> 7 & 0x7f | 0x80 ).chr + ( int & 0x7f ).chr
  elsif int < 0x200000
    data = ( int >> 14 & 0x7f | 0x80 ).chr + ( int >> 7 & 0x7f | 0x80 ).chr + ( int & 0x7f ).chr
  else
    data = ( int >> 22 & 0x7f | 0x80 ).chr + ( int >> 15 & 0x7f | 0x80 ).chr + ( int >> 8 & 0x7f | 0x80 ).chr + ( int & 0xff ).chr
  end

  @data += data
end
writeAMF3Number( number ) click to toggle source
# File lib/amf/serializer.rb, line 247
def writeAMF3Number( number )
  # can only handle signed 29bit ints
  if number >= -2^28 or number <= 2^28
    writeByte( AMF3_INTEGER_MARKER )
    writeAMF3Int( number )
  else
    writeByte( AMF3_DOUBLE_MARKER )
    writeDouble( number )
  end
end
writeAMF3String( string ) click to toggle source
# File lib/amf/serializer.rb, line 160
def writeAMF3String( string )
  if string.empty?
    writeByte( AMF3_NULL_MARKER )
  elsif ! handleReference( string, @storedStrings )
    writeAMF3Int( ( string.length << 1 | 1 ) )
    @data += string
  end
end
writeAMF3TypedObject( data ) click to toggle source
# File lib/amf/serializer.rb, line 197
def writeAMF3TypedObject( data )
  writeByte( AMF3_OBJECT_MARKER )
  if ! handleReference( data, @storedObjects )
    classname = data.send( AMF_FIELD_EXPLICIT_TYPE )
    if @className2TraitsInfo[ classname ].nil?
      propertyNames = Array.new
      data.marshal_dump.each do |key, value|
        if key[0] != "\0" and key.to_s != AMF_FIELD_EXPLICIT_TYPE
          propertyNames << key
        end
      end

      writeAMF3Int( propertyNames.count << 4 | 3 )
      writeAMF3String( classname )
      propertyNames.each do |p|
        writeAMF3String( p.to_s )
      end

      traitsInfo = { 'referenceId' => @className2TraitsInfo.keys.count, 'propertyNames' => propertyNames }
      @className2TraitsInfo[ classname ] = traitsInfo
    else
      traitsInfo = @className2TraitsInfo[ classname ]
      referenceId = traitsInfo[ 'referenceId' ]
      propertyNames = traitsInfo[ 'propertyNames' ]
      writeAMF3Int( referenceId << 2 | 1 )
    end

    propertyNames.each do |p|
      writeAMF3Data( data.marshal_dump[ p ] )
    end
  end
end
writeBoolean( bit ) click to toggle source
# File lib/amf/serializer.rb, line 117
def writeBoolean( bit )
  writeByte( AMF0_BOOLEAN_MARKER )
  writeByte( bit )
end
writeByte( byte ) click to toggle source
# File lib/amf/serializer.rb, line 91
def writeByte( byte )
  @data << [ byte ].pack( 'c' )
end
writeData( data ) click to toggle source
# File lib/amf/serializer.rb, line 51
def writeData( data )
  if @amfVersion == AMF3_VERSION
    writeByte( AMF0_AMF3_MARKER )
    writeAMF3Data( data )
  else
    raise "Unsupported writing AMF0 types"
  end
end
writeDouble( double ) click to toggle source
# File lib/amf/serializer.rb, line 103
def writeDouble( double )
 @data << [ double ].pack( 'd' ).reverse
end
writeInt( int ) click to toggle source
# File lib/amf/serializer.rb, line 95
def writeInt( int )
  @data << [ int ].pack( 'n' )
end
writeLong( long ) click to toggle source
# File lib/amf/serializer.rb, line 99
def writeLong( long )
  @data << [ long ].pack( 'N' )
end
writeLongUTF( string ) click to toggle source
# File lib/amf/serializer.rb, line 112
def writeLongUTF( string )
  writeLong( string.length )
  @data << string
end
writeNull() click to toggle source
# File lib/amf/serializer.rb, line 137
def writeNull
  writeByte( AMF0_NULL_MARKER )
end
writeNumber( number ) click to toggle source
# File lib/amf/serializer.rb, line 132
def writeNumber( number )
  writeByte( AMF0_NUMBER_MARKER )
  writeDouble( number.to_f )
end
writeString( string ) click to toggle source
# File lib/amf/serializer.rb, line 122
def writeString( string )
  if string.count < 65536
    writeByte( AMF0_STRING_MARKER )
    writeUFT( string )
  else
    writeByte( AMF0_LONG_STRING_MARKER )
    writeLongUTF( string )
  end
end
writeUTF( string ) click to toggle source
# File lib/amf/serializer.rb, line 107
def writeUTF( string )
  writeInt( string.length )
  @data << string
end
writeUndefined() click to toggle source
# File lib/amf/serializer.rb, line 141
def writeUndefined
  writeByte( AMF0_UNDEFINED_MARKER )
end