Skip to content

Commit

Permalink
Add new AS3 native types from Flash 10/Air 1.5
Browse files Browse the repository at this point in the history
This includes typed vectors for various integer types, and a more
densely packed (in comparison to Array) dictionary.
  • Loading branch information
fmoo committed Jan 5, 2015
1 parent 73edee3 commit 67d2bf2
Showing 1 changed file with 165 additions and 0 deletions.
165 changes: 165 additions & 0 deletions pyamf/amf3.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@
#: @see: U{Parsing ByteArrays on OSFlash (external)
#: <http://osflash.org/documentation/amf3/parsing_byte_arrays>}
TYPE_BYTEARRAY = '\x0C'
#: Vector types were added to ActionScript 3.0 in Flash Player 10.
TYPE_INT_VECTOR = '\x0D'
TYPE_UINT_VECTOR = '\x0E'
TYPE_DOUBLE_VECTOR = '\x0F'
TYPE_OBJECT_VECTOR = '\x10'
#: A native Dictionary type with object keys and values was added in
#: Flash Player 10 as well.
TYPE_DICTIONARY = '\x11'

#: Reference bit.
REFERENCE_BIT = 0x01
Expand Down Expand Up @@ -552,6 +560,53 @@ def compress(self):
self.compressed = True


class BaseVector(list):
fixed = False
def __repr__(self):
return "%s(%s, %s)" % (self.__class__.__name__,
super(BaseVector, self).__repr__(), self._get_attributes())

def _get_attributes(self):
return 'fixed=%s' % repr(self.fixed)


class IntVector(BaseVector):
datatype = TYPE_INT_VECTOR
reader = lambda self, decoder: decoder.stream.read_long
writer = lambda self, encoder: encoder.stream.write_long


class UintVector(BaseVector):
datatype = TYPE_UINT_VECTOR
reader = lambda self, decoder: decoder.stream.read_ulong
writer = lambda self, encoder: encoder.stream.write_ulong


class DoubleVector(BaseVector):
datatype = TYPE_DOUBLE_VECTOR
reader = lambda self, decoder: decoder.stream.read_double
writer = lambda self, encoder: encoder.stream.write_double


class ObjectVector(BaseVector):
classname = None
datatype = TYPE_OBJECT_VECTOR
reader = lambda self, decoder: decoder.readElement
writer = lambda self, encoder: encoder.writeElement

def _get_attributes(self):
return (super(ObjectVector, self)._get_attributes() +
', classname=' + repr(self.classname))


class ASDictionary(dict):
weak_keys = False

def __repr__(self):
return '%s(%s)' % (self.__class__.__name__,
super(ASDictionary, self).__repr__())


class ClassDefinition(object):
"""
This is an internal class used by L{Encoder}/L{Decoder} to hold details
Expand Down Expand Up @@ -783,6 +838,16 @@ def getTypeFunc(self, data):
return self.readXMLString
elif data == TYPE_BYTEARRAY:
return self.readByteArray
elif data == TYPE_INT_VECTOR:
return self.readIntVector
elif data == TYPE_UINT_VECTOR:
return self.readUintVector
elif data == TYPE_DOUBLE_VECTOR:
return self.readDoubleVector
elif data == TYPE_OBJECT_VECTOR:
return self.readObjectVector
elif data == TYPE_DICTIONARY:
return self.readASDictionary

def readProxy(self, obj):
"""
Expand Down Expand Up @@ -944,6 +1009,27 @@ def readArray(self):

return result

def readASDictionary(self):
length, is_reference = self._readLength()

# AS3 does not allow Dictionary types to have references
assert not is_reference

result = ASDictionary()

# Set weak-keys property
weak_keys = self.stream.read_uchar()
assert weak_keys in [0x00, 0x01]
result.weak_keys = bool(weak_keys)

# Read key-value pairs
for i in xrange(length):
key = self.readElement()
value = self.readElement()
result[key] = value

return result

def _getClassDefinition(self, ref):
"""
Reads class definition from the stream.
Expand Down Expand Up @@ -1105,6 +1191,47 @@ def readByteArray(self):

return obj

def readVector(self, vector_class):
"""
Reads an array of a specific datatype.
@see: L{TypedVector}
@note: This is not supported in ActionScript 1.0, 2.0, or early
versions of 3.0
"""
ref = self.readInteger(False)

if ref & REFERENCE_BIT == 0:
return self.context.getObject(ref >> 1)

obj = vector_class()

# This metadata indicates whether the (de)serialized object should
# use a fixed memory allocation. For python, this is semi-pointless,
# but we will need it to properly re-serialize the data.
obj.fixed = self.stream.read_uchar()

if isinstance(obj, ObjectVector):
obj.classname = self.readString()

num_items = ref >> 1
for i in xrange(num_items):
obj.append(obj.reader(self)())

return obj

def readIntVector(self):
return self.readVector(IntVector)

def readUintVector(self):
return self.readVector(UintVector)

def readDoubleVector(self):
return self.readVector(DoubleVector)

def readObjectVector(self):
return self.readVector(ObjectVector)


class Encoder(codec.Encoder):
"""
Expand Down Expand Up @@ -1132,6 +1259,10 @@ def getTypeFunc(self, data):
return self.writeByteArray
elif t is pyamf.MixedArray:
return self.writeDict
elif isinstance(data, BaseVector):
return self.writeVector
elif t is ASDictionary:
return self.writeASDictionary

return codec.Encoder.getTypeFunc(self, data)

Expand Down Expand Up @@ -1502,6 +1633,40 @@ def writeByteArray(self, n):
self._writeInteger(l << 1 | REFERENCE_BIT)
self.stream.write(buf)

def writeVector(self, n):
assert isinstance(n, BaseVector)

self.stream.write(n.datatype)

ref = self.context.getObjectReference(n)

if ref != -1:
self._writeInteger(ref << 1)

return

self.context.addObject(n)

self._writeInteger(len(n) << 1 | REFERENCE_BIT)
self.stream.write_uchar(1 if n.fixed else 0)

if isinstance(n, ObjectVector):
self.writeString(n.classname)

for item in n:
n.writer(self)(item)

def writeASDictionary(self, n):
assert isinstance(n, ASDictionary)

self.stream.write(TYPE_DICTIONARY)
self._writeInteger(len(n) << 1 | REFERENCE_BIT)
self.stream.write_uchar(0x01 if n.weak_keys else 0x00)

for key, value in n.iteritems():
self.writeElement(key)
self.writeElement(value)

def writeXML(self, n):
"""
Writes a XML string to the data stream.
Expand Down

0 comments on commit 67d2bf2

Please sign in to comment.