From 67d2bf2a0da9b940d96cff6cc98156349cad276f Mon Sep 17 00:00:00 2001 From: Peter Ruibal Date: Sat, 3 Jan 2015 19:35:56 -0800 Subject: [PATCH 1/3] Add new AS3 native types from Flash 10/Air 1.5 This includes typed vectors for various integer types, and a more densely packed (in comparison to Array) dictionary. --- pyamf/amf3.py | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) diff --git a/pyamf/amf3.py b/pyamf/amf3.py index d4d463a1..05e8b3ce 100644 --- a/pyamf/amf3.py +++ b/pyamf/amf3.py @@ -108,6 +108,14 @@ #: @see: U{Parsing ByteArrays on OSFlash (external) #: } 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 @@ -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 @@ -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): """ @@ -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. @@ -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): """ @@ -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) @@ -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. From 3d8f48dac5b6917edb0b69b432ba7d87ba513e05 Mon Sep 17 00:00:00 2001 From: Tamas Nepusz Date: Tue, 31 Mar 2015 16:17:41 +0200 Subject: [PATCH 2/3] adding support for Flash Player 10 types in cpyamf --- cpyamf/amf3.pxd | 8 ++++ cpyamf/amf3.pyx | 115 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/cpyamf/amf3.pxd b/cpyamf/amf3.pxd index a32ef444..f7cd9e4f 100644 --- a/cpyamf/amf3.pxd +++ b/cpyamf/amf3.pxd @@ -47,10 +47,16 @@ cdef class Decoder(codec.Decoder): cdef int _readStatic(self, ClassDefinition class_def, dict obj) except -1 cdef int _readDynamic(self, ClassDefinition class_def, dict obj) except -1 + cdef object readASDictionary(self) cdef object readBytes(self) cdef object readInteger(self, int signed=?) cdef object readByteArray(self) + cdef object readDoubleVector(self) + cdef object readIntVector(self) + cdef object readObjectVector(self) cdef object readProxy(self, obj) + cdef object readUintVector(self) + cdef object readVector(self, vector_class) cdef class Encoder(codec.Encoder): @@ -59,3 +65,5 @@ cdef class Encoder(codec.Encoder): cdef int writeByteArray(self, object obj) except -1 cdef int writeProxy(self, obj) except -1 + cdef int writeVector(self, object obj) except -1 + cdef int writeASDictionary(self, object obj) except -1 diff --git a/cpyamf/amf3.pyx b/cpyamf/amf3.pyx index 1b587c47..d7bc8557 100644 --- a/cpyamf/amf3.pyx +++ b/cpyamf/amf3.pyx @@ -38,6 +38,11 @@ cdef char TYPE_ARRAY = '\x09' cdef char TYPE_OBJECT = '\x0A' cdef char TYPE_XMLSTRING = '\x0B' cdef char TYPE_BYTEARRAY = '\x0C' +cdef char TYPE_INT_VECTOR = '\x0D' +cdef char TYPE_UINT_VECTOR = '\x0E' +cdef char TYPE_DOUBLE_VECTOR = '\x0F' +cdef char TYPE_OBJECT_VECTOR = '\x10' +cdef char TYPE_DICTIONARY = '\x11' cdef unsigned int REFERENCE_BIT = 0x01 cdef char REF_CHAR = '\x01' @@ -54,6 +59,12 @@ cdef int OBJECT_ENCODING_DYNAMIC = 0x02 cdef int OBJECT_ENCODING_PROXY = 0x03 cdef object ByteArrayType = amf3.ByteArray +cdef object BaseVectorType = amf3.BaseVector +cdef object IntVectorType = amf3.IntVector +cdef object UintVectorType = amf3.UintVector +cdef object DoubleVectorType = amf3.DoubleVector +cdef object ObjectVectorType = amf3.ObjectVector +cdef object ASDictionaryType = amf3.ASDictionary cdef object DataInput = amf3.DataInput cdef object DataOutput = amf3.DataOutput cdef str empty_string = str('') @@ -559,6 +570,62 @@ cdef class Decoder(codec.Decoder): """ return self.context.getObjectForProxy(obj) + cdef object readVector(self, vector_class): + """ + Reads a vector of a specific datatype. + + @note: This is not supported in ActionScript 1.0, 2.0 or early + versions of 3.0. + """ + cdef int ref = _read_ref(self.stream) + cdef int i + + if ref & REFERENCE_BIT == 0: + return self.context.getObject(ref >> 1) + + cdef object obj = vector_class() + obj.fixed = self.stream.read_uchar() + + if isinstance(obj, ObjectVectorType): + obj.classname = self.readString() + + cdef int num_items = ref >> 1 + for 0 <= i < num_items: + obj.append(obj.reader(self)()) + + return obj + + cdef object readIntVector(self): + return self.readVector(IntVectorType) + + cdef object readUintVector(self): + return self.readVector(UintVectorType) + + cdef object readDoubleVector(self): + return self.readVector(DoubleVectorType) + + cdef object readObjectVector(self): + return self.readVector(ObjectVectorType) + + cdef object readASDictionary(self): + cdef int ref = _read_ref(self.stream) + cdef int i + assert ref & REFERENCE_BIT + + ref >>= 1 + + result = ASDictionaryType() + weak_keys = self.stream.read_uchar() + assert weak_keys in [0x00, 0x01] + result.weak_keys = bool(weak_keys) + + for 0 <= i < ref: + key = self.readElement() + value = self.readElement() + result[key] = value + + return result + cdef object readConcreteElement(self, char t): if t == TYPE_STRING: return self.readString() @@ -586,6 +653,16 @@ cdef class Decoder(codec.Decoder): return self.readXML() elif t == TYPE_XMLSTRING: return self.readXML() + elif t == TYPE_INT_VECTOR: + return self.readIntVector() + elif t == TYPE_UINT_VECTOR: + return self.readUintVector() + elif t == TYPE_DOUBLE_VECTOR: + return self.readDoubleVector() + elif t == TYPE_OBJECT_VECTOR: + return self.readObjectVector() + elif t == TYPE_DICTIONARY: + return self.readASDictionary() raise pyamf.DecodeError("Unsupported ActionScript type") @@ -1026,12 +1103,50 @@ cdef class Encoder(codec.Encoder): return self.writeObject(proxy, 1) + cdef int writeVector(self, object n) except -1: + cdef Py_ssize_t ref = self.context.getObjectReference(n) + + self.writeType(ord(n.datatype)) + + if ref != -1: + _encode_integer(self.stream, ref << 1) + return 0 + + self.context.addObject(n) + _encode_integer(self.stream, (len(n) << 1) | REFERENCE_BIT) + self.stream.write_uchar(1 if n.fixed else 0) + + if isinstance(n, ObjectVectorType): + self.serialiseString(n.classname) + + for item in n: + n.writer(self)(item) + + cdef int writeASDictionary(self, object n) except -1: + self.writeType(TYPE_DICTIONARY) + _encode_integer(self.stream, (len(n) << 1) | REFERENCE_BIT) + self.stream.write_uchar(1 if n.weak_keys else 0) + + for key, value in n.iteritems(): + self.writeElement(key) + self.writeElement(value) + cdef int handleBasicTypes(self, object element, object py_type) except -1: cdef int ret = codec.Encoder.handleBasicTypes(self, element, py_type) if ret == 1: # not handled if py_type is ByteArrayType: return self.writeByteArray(element) + elif py_type is IntVectorType: + return self.writeVector(element) + elif py_type is UintVectorType: + return self.writeVector(element) + elif py_type is DoubleVectorType: + return self.writeVector(element) + elif py_type is ObjectVectorType: + return self.writeVector(element) + elif py_type is ASDictionaryType: + return self.writeASDictionary(element) return ret From b692419cb1718fd30629ad6ee040870cd03175bc Mon Sep 17 00:00:00 2001 From: mastergenius Date: Mon, 24 Oct 2016 15:30:36 +0300 Subject: [PATCH 3/3] Fix code style --- pyamf/amf3.py | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/pyamf/amf3.py b/pyamf/amf3.py index e94982cc..4ac94272 100644 --- a/pyamf/amf3.py +++ b/pyamf/amf3.py @@ -565,9 +565,12 @@ def compress(self): class BaseVector(list): fixed = False + def __repr__(self): - return "%s(%s, %s)" % (self.__class__.__name__, - super(BaseVector, self).__repr__(), self._get_attributes()) + return "%s(%s, %s)" % ( + self.__class__.__name__, + super(BaseVector, self).__repr__(), + self._get_attributes()) def _get_attributes(self): return 'fixed=%s' % repr(self.fixed) @@ -575,30 +578,47 @@ def _get_attributes(self): class IntVector(BaseVector): datatype = TYPE_INT_VECTOR - reader = lambda self, decoder: decoder.stream.read_long - writer = lambda self, encoder: encoder.stream.write_long + + def reader(self, decoder): + return decoder.stream.read_long + + def writer(self, encoder): + return 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 + + def reader(self, decoder): + return decoder.stream.read_ulong + + def writer(self, encoder): + return 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 + + def reader(self, decoder): + return decoder.stream.read_double + + def writer(self, encoder): + return 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 reader(self, decoder): + return decoder.readElement + + def writer(self, encoder): + return encoder.writeElement def _get_attributes(self): - return (super(ObjectVector, self)._get_attributes() + + return ( + super(ObjectVector, self)._get_attributes() + ', classname=' + repr(self.classname)) @@ -606,7 +626,8 @@ class ASDictionary(dict): weak_keys = False def __repr__(self): - return '%s(%s)' % (self.__class__.__name__, + return '%s(%s)' % ( + self.__class__.__name__, super(ASDictionary, self).__repr__())