Package pyamf :: Module amf3
[hide private]
[frames] | no frames]

Source Code for Module pyamf.amf3

   1  # -*- coding: utf-8 -*- 
   2  # 
   3  # Copyright (c) 2007-2009 The PyAMF Project. 
   4  # See LICENSE.txt for details. 
   5   
   6  """ 
   7  AMF3 implementation. 
   8   
   9  C{AMF3} is the default serialization for 
  10  U{ActionScript<http://en.wikipedia.org/wiki/ActionScript>} 3.0 and provides 
  11  various advantages over L{AMF0<pyamf.amf0>}, which is used for ActionScript 1.0 
  12  and 2.0. It adds support for sending C{int} and C{uint} objects as integers and 
  13  supports data types that are available only in ActionScript 3.0, such as 
  14  L{ByteArray} and L{ArrayCollection}. 
  15   
  16  @see: U{Official AMF3 Specification in English (external) 
  17  <http://opensource.adobe.com/wiki/download/attachments/1114283/amf3_spec_05_05_08.pdf>} 
  18  @see: U{Official AMF3 Specification in Japanese (external) 
  19  <http://opensource.adobe.com/wiki/download/attachments/1114283/JP_amf3_spec_121207.pdf>} 
  20  @see: U{AMF3 documentation on OSFlash (external) 
  21  <http://osflash.org/documentation/amf3>} 
  22   
  23  @since: 0.1 
  24  """ 
  25   
  26  import types 
  27  import datetime 
  28  import zlib 
  29   
  30  import pyamf 
  31  from pyamf import util 
  32  from pyamf.flex import ObjectProxy, ArrayCollection 
  33   
  34  #: If True encode/decode lists/tuples to L{ArrayCollections<ArrayCollection>} 
  35  #: and dicts to L{ObjectProxy} 
  36  use_proxies_default = False 
  37   
  38  try: 
  39      set() 
  40  except NameError: 
  41      from sets import Set as set 
  42   
  43   
  44  #: The undefined type is represented by the undefined type marker. No further 
  45  #: information is encoded for this value. 
  46  TYPE_UNDEFINED = '\x00' 
  47  #: The null type is represented by the null type marker. No further 
  48  #: information is encoded for this value. 
  49  TYPE_NULL = '\x01' 
  50  #: The false type is represented by the false type marker and is used to 
  51  #: encode a Boolean value of C{false}. No further information is encoded for 
  52  #: this value. 
  53  TYPE_BOOL_FALSE = '\x02' 
  54  #: The true type is represented by the true type marker and is used to encode 
  55  #: a Boolean value of C{true}. No further information is encoded for this 
  56  #: value. 
  57  TYPE_BOOL_TRUE = '\x03' 
  58  #: In AMF 3 integers are serialized using a variable length unsigned 29-bit 
  59  #: integer. 
  60  #: @see: U{Parsing Integers on OSFlash (external) 
  61  #: <http://osflash.org/documentation/amf3/parsing_integers>} 
  62  TYPE_INTEGER = '\x04' 
  63  #: This type is used to encode an ActionScript Number or an ActionScript 
  64  #: C{int} of value greater than or equal to 2^28 or an ActionScript uint of 
  65  #: value greater than or equal to 2^29. The encoded value is is always an 8 
  66  #: byte IEEE-754 double precision floating point value in network byte order 
  67  #: (sign bit in low memory). The AMF 3 number type is encoded in the same 
  68  #: manner as the AMF 0 L{Number<pyamf.amf0.TYPE_NUMBER>} type. 
  69  TYPE_NUMBER = '\x05' 
  70  #: ActionScript String values are represented using a single string type in 
  71  #: AMF 3 - the concept of string and long string types from AMF 0 is not used. 
  72  #: Strings can be sent as a reference to a previously occurring String by 
  73  #: using an index to the implicit string reference table. Strings are encoding 
  74  #: using UTF-8 - however the header may either describe a string literal or a 
  75  #: string reference. 
  76  TYPE_STRING = '\x06' 
  77  #: ActionScript 3.0 introduced a new XML type however the legacy C{XMLDocument} 
  78  #: type from ActionScript 1.0 and 2.0.is retained in the language as 
  79  #: C{flash.xml.XMLDocument}. Similar to AMF 0, the structure of an 
  80  #: C{XMLDocument} needs to be flattened into a string representation for 
  81  #: serialization. As with other strings in AMF, the content is encoded in 
  82  #: UTF-8. XMLDocuments can be sent as a reference to a previously occurring 
  83  #: C{XMLDocument} instance by using an index to the implicit object reference 
  84  #: table. 
  85  #: @see: U{OSFlash documentation (external) 
  86  #: <http://osflash.org/documentation/amf3#x07_-_xml_legacy_flash.xml.xmldocument_class>} 
  87  TYPE_XML = '\x07' 
  88  #: In AMF 3 an ActionScript Date is serialized simply as the number of 
  89  #: milliseconds elapsed since the epoch of midnight, 1st Jan 1970 in the 
  90  #: UTC time zone. Local time zone information is not sent. 
  91  TYPE_DATE = '\x08' 
  92  #: ActionScript Arrays are described based on the nature of their indices, 
  93  #: i.e. their type and how they are positioned in the Array. 
  94  TYPE_ARRAY = '\x09' 
  95  #: A single AMF 3 type handles ActionScript Objects and custom user classes. 
  96  TYPE_OBJECT = '\x0A' 
  97  #: ActionScript 3.0 introduces a new top-level XML class that supports 
  98  #: U{E4X<http://en.wikipedia.org/wiki/E4X>} syntax. 
  99  #: For serialization purposes the XML type needs to be flattened into a 
 100  #: string representation. As with other strings in AMF, the content is 
 101  #: encoded using UTF-8. 
 102  TYPE_XMLSTRING = '\x0B' 
 103  #: ActionScript 3.0 introduces the L{ByteArray} type to hold an Array 
 104  #: of bytes. AMF 3 serializes this type using a variable length encoding 
 105  #: 29-bit integer for the byte-length prefix followed by the raw bytes 
 106  #: of the L{ByteArray}. 
 107  #: @see: U{Parsing ByteArrays on OSFlash (external) 
 108  #: <http://osflash.org/documentation/amf3/parsing_byte_arrays>} 
 109  TYPE_BYTEARRAY = '\x0C' 
 110   
 111  #: Reference bit. 
 112  REFERENCE_BIT = 0x01 
 113   
 114  #: The maximum that can be represented by an unsigned 29 bit integer. 
 115  MAX_29B_INT = 0x3FFFFFFF 
 116   
 117  ENCODED_INT_CACHE = {} 
 118   
 119   
120 -class ObjectEncoding:
121 """ 122 AMF object encodings. 123 """ 124 #: Property list encoding. 125 #: The remaining integer-data represents the number of class members that 126 #: exist. The property names are read as string-data. The values are then 127 #: read as AMF3-data. 128 STATIC = 0x00 129 130 #: Externalizable object. 131 #: What follows is the value of the "inner" object, including type code. 132 #: This value appears for objects that implement IExternalizable, such as 133 #: L{ArrayCollection} and L{ObjectProxy}. 134 EXTERNAL = 0x01 135 136 #: Name-value encoding. 137 #: The property names and values are encoded as string-data followed by 138 #: AMF3-data until there is an empty string property name. If there is a 139 #: class-def reference there are no property names and the number of values 140 #: is equal to the number of properties in the class-def. 141 DYNAMIC = 0x02 142 143 #: Proxy object. 144 PROXY = 0x03
145 146
147 -class DataOutput(object):
148 """ 149 I am a C{StringIO} type object containing byte data from the AMF stream. 150 ActionScript 3.0 introduced the C{flash.utils.ByteArray} class to support 151 the manipulation of raw data in the form of an Array of bytes. 152 I provide a set of methods for writing binary data with ActionScript 3.0. 153 154 This class is the I/O counterpart to the L{DataInput} class, which reads 155 binary data. 156 157 @see: U{IDataOutput on Livedocs (external) 158 <http://livedocs.adobe.com/flex/201/langref/flash/utils/IDataOutput.html>} 159 """
160 - def __init__(self, encoder):
161 """ 162 @param encoder: Encoder containing the stream. 163 @type encoder: L{amf3.Encoder<pyamf.amf3.Encoder>} 164 """ 165 self.encoder = encoder 166 self.stream = encoder.stream
167
168 - def writeBoolean(self, value):
169 """ 170 Writes a Boolean value. 171 172 @type value: C{bool} 173 @param value: A C{Boolean} value determining which byte is written. 174 If the parameter is C{True}, C{1} is written; if C{False}, C{0} is 175 written. 176 177 @raise ValueError: Non-boolean value found. 178 """ 179 if isinstance(value, bool): 180 if value is True: 181 self.stream.write_uchar(1) 182 else: 183 self.stream.write_uchar(0) 184 else: 185 raise ValueError("Non-boolean value found")
186
187 - def writeByte(self, value):
188 """ 189 Writes a byte. 190 191 @type value: C{int} 192 """ 193 self.stream.write_char(value)
194
195 - def writeUnsignedByte(self, value):
196 """ 197 Writes an unsigned byte. 198 199 @type value: C{int} 200 @since: 0.5 201 """ 202 return self.stream.write_uchar(value)
203
204 - def writeDouble(self, value):
205 """ 206 Writes an IEEE 754 double-precision (64-bit) floating 207 point number. 208 209 @type value: C{number} 210 """ 211 self.stream.write_double(value)
212
213 - def writeFloat(self, value):
214 """ 215 Writes an IEEE 754 single-precision (32-bit) floating 216 point number. 217 218 @type value: C{float} 219 """ 220 self.stream.write_float(value)
221
222 - def writeInt(self, value):
223 """ 224 Writes a 32-bit signed integer. 225 226 @type value: C{int} 227 """ 228 self.stream.write_long(value)
229
230 - def writeMultiByte(self, value, charset):
231 """ 232 Writes a multibyte string to the datastream using the 233 specified character set. 234 235 @type value: C{str} 236 @param value: The string value to be written. 237 @type charset: C{str} 238 @param charset: The string denoting the character set to use. Possible 239 character set strings include C{shift-jis}, C{cn-gb}, 240 C{iso-8859-1} and others. 241 @see: U{Supported character sets on Livedocs (external) 242 <http://livedocs.adobe.com/flex/201/langref/charset-codes.html>} 243 """ 244 self.stream.write(unicode(value).encode(charset))
245
246 - def writeObject(self, value, use_references=True, use_proxies=None):
247 """ 248 Writes an object to data stream in AMF serialized format. 249 250 @param value: The object to be serialized. 251 @type use_references: C{bool} 252 @param use_references: 253 """ 254 self.encoder.writeElement(value, use_references, use_proxies)
255
256 - def writeShort(self, value):
257 """ 258 Writes a 16-bit integer. 259 260 @type value: C{int} 261 @param value: A byte value as an integer. 262 """ 263 self.stream.write_short(value)
264
265 - def writeUnsignedShort(self, value):
266 """ 267 Writes a 16-bit unsigned integer. 268 269 @type value: C{int} 270 @param value: A byte value as an integer. 271 @since: 0.5 272 """ 273 self.stream.write_ushort(value)
274
275 - def writeUnsignedInt(self, value):
276 """ 277 Writes a 32-bit unsigned integer. 278 279 @type value: C{int} 280 @param value: A byte value as an unsigned integer. 281 """ 282 self.stream.write_ulong(value)
283
284 - def writeUTF(self, value):
285 """ 286 Writes a UTF-8 string to the data stream. 287 288 The length of the UTF-8 string in bytes is written first, 289 as a 16-bit integer, followed by the bytes representing the 290 characters of the string. 291 292 @type value: C{str} 293 @param value: The string value to be written. 294 """ 295 if not isinstance(value, unicode): 296 value = unicode(value, 'utf8') 297 298 buf = util.BufferedByteStream() 299 buf.write_utf8_string(value) 300 bytes = buf.getvalue() 301 302 self.stream.write_ushort(len(bytes)) 303 self.stream.write(bytes)
304
305 - def writeUTFBytes(self, value):
306 """ 307 Writes a UTF-8 string. Similar to L{writeUTF}, but does 308 not prefix the string with a 16-bit length word. 309 310 @type value: C{str} 311 @param value: The string value to be written. 312 """ 313 val = None 314 315 if isinstance(value, unicode): 316 val = value 317 else: 318 val = unicode(value, 'utf8') 319 320 self.stream.write_utf8_string(val)
321 322
323 -class DataInput(object):
324 """ 325 I provide a set of methods for reading binary data with ActionScript 3.0. 326 327 This class is the I/O counterpart to the L{DataOutput} class, 328 which writes binary data. 329 330 @see: U{IDataInput on Livedocs (external) 331 <http://livedocs.adobe.com/flex/201/langref/flash/utils/IDataInput.html>} 332 """ 333
334 - def __init__(self, decoder):
335 """ 336 @param decoder: AMF3 decoder containing the stream. 337 @type decoder: L{amf3.Decoder<pyamf.amf3.Decoder>} 338 """ 339 assert isinstance(decoder, Decoder) 340 341 self.decoder = decoder 342 self.stream = decoder.stream
343
344 - def readBoolean(self):
345 """ 346 Read C{Boolean}. 347 348 @raise ValueError: Error reading Boolean. 349 @rtype: C{bool} 350 @return: A Boolean value, C{True} if the byte 351 is nonzero, C{False} otherwise. 352 """ 353 byte = self.stream.read(1) 354 355 if byte == '\x00': 356 return False 357 elif byte == '\x01': 358 return True 359 else: 360 raise ValueError("Error reading boolean")
361
362 - def readByte(self):
363 """ 364 Reads a signed byte. 365 366 @rtype: C{int} 367 @return: The returned value is in the range -128 to 127. 368 """ 369 return self.stream.read_char()
370
371 - def readDouble(self):
372 """ 373 Reads an IEEE 754 double-precision floating point number from the 374 data stream. 375 376 @rtype: C{number} 377 @return: An IEEE 754 double-precision floating point number. 378 """ 379 return self.stream.read_double()
380
381 - def readFloat(self):
382 """ 383 Reads an IEEE 754 single-precision floating point number from the 384 data stream. 385 386 @rtype: C{number} 387 @return: An IEEE 754 single-precision floating point number. 388 """ 389 return self.stream.read_float()
390
391 - def readInt(self):
392 """ 393 Reads a signed 32-bit integer from the data stream. 394 395 @rtype: C{int} 396 @return: The returned value is in the range -2147483648 to 2147483647. 397 """ 398 return self.stream.read_long()
399
400 - def readMultiByte(self, length, charset):
401 """ 402 Reads a multibyte string of specified length from the data stream 403 using the specified character set. 404 405 @type length: C{int} 406 @param length: The number of bytes from the data stream to read. 407 @type charset: C{str} 408 @param charset: The string denoting the character set to use. 409 410 @rtype: C{str} 411 @return: UTF-8 encoded string. 412 """ 413 #FIXME nick: how to work out the code point byte size (on the fly)? 414 bytes = self.stream.read(length) 415 416 return unicode(bytes, charset)
417
418 - def readObject(self):
419 """ 420 Reads an object from the data stream. 421 422 @return: The deserialized object. 423 """ 424 return self.decoder.readElement()
425
426 - def readShort(self):
427 """ 428 Reads a signed 16-bit integer from the data stream. 429 430 @rtype: C{uint} 431 @return: The returned value is in the range -32768 to 32767. 432 """ 433 return self.stream.read_short()
434
435 - def readUnsignedByte(self):
436 """ 437 Reads an unsigned byte from the data stream. 438 439 @rtype: C{uint} 440 @return: The returned value is in the range 0 to 255. 441 """ 442 return self.stream.read_uchar()
443
444 - def readUnsignedInt(self):
445 """ 446 Reads an unsigned 32-bit integer from the data stream. 447 448 @rtype: C{uint} 449 @return: The returned value is in the range 0 to 4294967295. 450 """ 451 return self.stream.read_ulong()
452
453 - def readUnsignedShort(self):
454 """ 455 Reads an unsigned 16-bit integer from the data stream. 456 457 @rtype: C{uint} 458 @return: The returned value is in the range 0 to 65535. 459 """ 460 return self.stream.read_ushort()
461
462 - def readUTF(self):
463 """ 464 Reads a UTF-8 string from the data stream. 465 466 The string is assumed to be prefixed with an unsigned 467 short indicating the length in bytes. 468 469 @rtype: C{str} 470 @return: A UTF-8 string produced by the byte 471 representation of characters. 472 """ 473 length = self.stream.read_ushort() 474 return self.stream.read_utf8_string(length)
475
476 - def readUTFBytes(self, length):
477 """ 478 Reads a sequence of C{length} UTF-8 bytes from the data 479 stream and returns a string. 480 481 @type length: C{int} 482 @param length: The number of bytes from the data stream to read. 483 @rtype: C{str} 484 @return: A UTF-8 string produced by the byte representation of 485 characters of specified C{length}. 486 """ 487 return self.readMultiByte(length, 'utf-8')
488 489
490 -class ByteArray(util.BufferedByteStream, DataInput, DataOutput):
491 """ 492 I am a C{StringIO} type object containing byte data from the AMF stream. 493 ActionScript 3.0 introduced the C{flash.utils.ByteArray} class to support 494 the manipulation of raw data in the form of an Array of bytes. 495 496 Supports C{zlib} compression. 497 498 Possible uses of the C{ByteArray} class: 499 - Creating a custom protocol to connect to a client. 500 - Writing your own AMF/Remoting packet. 501 - Optimizing the size of your data by using custom data types. 502 503 @see: U{ByteArray on Livedocs (external) 504 <http://livedocs.adobe.com/flex/201/langref/flash/utils/ByteArray.html>} 505 """ 506
507 - class __amf__:
508 amf3 = True
509
510 - def __init__(self, *args, **kwargs):
511 self.context = kwargs.pop('context', Context()) 512 513 util.BufferedByteStream.__init__(self, *args, **kwargs) 514 DataInput.__init__(self, Decoder(self, self.context)) 515 DataOutput.__init__(self, Encoder(self, self.context)) 516 517 self.compressed = False
518
519 - def __cmp__(self, other):
520 if isinstance(other, ByteArray): 521 return cmp(self.getvalue(), other.getvalue()) 522 523 return cmp(self.getvalue(), other)
524
525 - def __str__(self):
526 buf = self.getvalue() 527 528 if self.compressed: 529 buf = zlib.compress(buf) 530 #FIXME nick: hacked 531 buf = buf[0] + '\xda' + buf[2:] 532 533 return buf
534
535 - def compress(self):
536 """ 537 Forces compression of the underlying stream. 538 """ 539 self.compressed = True
540 541
542 -class ClassDefinition(object):
543 """ 544 """ 545
546 - def __init__(self, alias):
547 self.alias = alias 548 self.reference = None 549 550 alias.compile() 551 552 self.attr_len = 0 553 554 if alias.static_attrs: 555 self.attr_len = len(alias.static_attrs) 556 557 self.encoding = ObjectEncoding.DYNAMIC 558 559 if alias.external: 560 self.encoding = ObjectEncoding.EXTERNAL 561 elif not alias.dynamic: 562 if alias.static_attrs == alias.encodable_properties: 563 self.encoding = ObjectEncoding.STATIC
564
565 - def __repr__(self):
566 return '<%s.ClassDefinition reference=%r encoding=%r alias=%r at 0x%x>' % ( 567 self.__class__.__module__, self.reference, self.encoding, self.alias, id(self))
568 569
570 -class Context(pyamf.BaseContext):
571 """ 572 I hold the AMF3 context for en/decoding streams. 573 574 @ivar strings: A list of string references. 575 @type strings: C{list} 576 @ivar classes: A list of L{ClassDefinition}. 577 @type classes: C{list} 578 @ivar legacy_xml: A list of legacy encoded XML documents. 579 @type legacy_xml: C{list} 580 """ 581
582 - def __init__(self, exceptions=True):
583 self.strings = util.IndexedCollection(use_hash=True, exceptions=False) 584 self.classes = {} 585 self.class_ref = {} 586 self.legacy_xml = util.IndexedCollection(exceptions=False) 587 self.object_aliases = util.IndexedMap(exceptions=False) # Maps one object to another 588 589 self.class_idx = 0 590 591 pyamf.BaseContext.__init__(self, exceptions=exceptions)
592
593 - def clear(self):
594 """ 595 Clears the context. 596 """ 597 pyamf.BaseContext.clear(self) 598 599 self.strings.clear() 600 self.classes = {} 601 self.class_ref = {} 602 self.legacy_xml.clear() 603 self.object_aliases.clear() 604 605 self.class_idx = 0
606
607 - def setObjectAlias(self, obj, alias):
608 """ 609 Maps an object to an aliased object. 610 611 @since: 0.4 612 """ 613 self.object_aliases.map(obj, alias)
614
615 - def getObjectAlias(self, obj):
616 """ 617 Get an alias of an object. 618 619 @since: 0.4 620 @raise pyamf.ReferenceError: Unknown object alias. 621 @raise pyamf.ReferenceError: Unknown mapped alias. 622 """ 623 ref = self.object_aliases.getReferenceTo(obj) 624 625 if ref is None: 626 if self.exceptions is False: 627 return None 628 629 raise pyamf.ReferenceError('Unknown object alias for %r' % (obj,)) 630 631 mapped = self.object_aliases.getMappedByReference(ref) 632 633 if mapped is None: 634 if self.exceptions is False: 635 return None 636 637 raise pyamf.ReferenceError('Unknown mapped alias for %r' % (obj,)) 638 639 return mapped
640
641 - def getString(self, ref):
642 """ 643 Gets a string based on a reference C{ref}. 644 645 @param ref: The reference index. 646 @type ref: C{str} 647 @raise pyamf.ReferenceError: The referenced string could not be found. 648 649 @rtype: C{str} 650 @return: The referenced string. 651 """ 652 i = self.strings.getByReference(ref) 653 654 if i is None and self.exceptions: 655 raise pyamf.ReferenceError("String reference %r not found" % (ref,)) 656 657 return i
658
659 - def getStringReference(self, s):
660 """ 661 Return string reference. 662 663 @type s: C{str} 664 @param s: The referenced string. 665 @raise pyamf.ReferenceError: The string reference could not be found. 666 @return: The reference index to the string. 667 @rtype: C{int} 668 """ 669 i = self.strings.getReferenceTo(s) 670 671 if i is None and self.exceptions: 672 raise pyamf.ReferenceError("Reference for string %r not found" % (s,)) 673 674 return i
675
676 - def addString(self, s):
677 """ 678 Creates a reference to C{s}. If the reference already exists, that 679 reference is returned. 680 681 @type s: C{str} 682 @param s: The string to be referenced. 683 @rtype: C{int} 684 @return: The reference index. 685 686 @raise TypeError: The parameter C{s} is not of C{basestring} type. 687 @raise pyamf.ReferenceError: Trying to store a reference to an empty string. 688 """ 689 if not isinstance(s, basestring): 690 raise TypeError 691 692 if len(s) == 0: 693 if not self.exceptions: 694 return None 695 696 # do not store empty string references 697 raise pyamf.ReferenceError("Cannot store a reference to an empty string") 698 699 return self.strings.append(s)
700
701 - def getClassByReference(self, ref):
702 """ 703 Return class reference. 704 705 @raise pyamf.ReferenceError: The class reference could not be found. 706 @return: Class reference. 707 """ 708 try: 709 return self.class_ref[ref] 710 except KeyError: 711 if not self.exceptions: 712 return None 713 714 raise pyamf.ReferenceError("Class reference %r not found" % ( 715 ref,))
716
717 - def getClass(self, klass):
718 """ 719 Return class reference. 720 721 @raise pyamf.ReferenceError: The class reference could not be found. 722 @return: Class reference. 723 """ 724 try: 725 return self.classes[klass] 726 except KeyError: 727 if not self.exceptions: 728 return None 729 730 raise pyamf.ReferenceError("Class alias for %r not found" % ( 731 klass,))
732
733 - def addClass(self, alias, klass):
734 """ 735 Creates a reference to C{class_def}. 736 737 @param alias: C{ClassDefinition} instance. 738 """ 739 ref = self.class_idx 740 741 self.class_ref[ref] = alias 742 cd = self.classes[klass] = alias 743 744 cd.reference = ref 745 746 self.class_idx += 1 747 748 return ref
749
750 - def getLegacyXML(self, ref):
751 """ 752 Return the legacy XML reference. This is the C{flash.xml.XMLDocument} 753 class in ActionScript 3.0 and the top-level C{XML} class in 754 ActionScript 1.0 and 2.0. 755 756 @type ref: C{int} 757 @param ref: The reference index. 758 @raise pyamf.ReferenceError: The legacy XML reference could not be found. 759 @return: Instance of L{ET<util.ET>} 760 """ 761 i = self.legacy_xml.getByReference(ref) 762 763 if i is None: 764 if not self.exceptions: 765 return None 766 767 raise pyamf.ReferenceError("Legacy XML reference %r not found" % (ref,)) 768 769 return i
770
771 - def getLegacyXMLReference(self, doc):
772 """ 773 Return legacy XML reference. 774 775 @type doc: L{ET<util.ET>} 776 @param doc: The XML document to reference. 777 @raise pyamf.ReferenceError: The reference could not be found. 778 @return: The reference to C{doc}. 779 @rtype: C{int} 780 """ 781 i = self.legacy_xml.getReferenceTo(doc) 782 783 if i is None: 784 if not self.exceptions: 785 return None 786 787 raise pyamf.ReferenceError("Reference for document %r not found" % (doc,)) 788 789 return i
790
791 - def addLegacyXML(self, doc):
792 """ 793 Creates a reference to C{doc}. 794 795 If C{doc} is already referenced that index will be returned. Otherwise 796 a new index will be created. 797 798 @type doc: L{ET<util.ET>} 799 @param doc: The XML document to reference. 800 @rtype: C{int} 801 @return: The reference to C{doc}. 802 """ 803 return self.legacy_xml.append(doc)
804
805 - def __copy__(self):
806 return self.__class__(exceptions=self.exceptions)
807 808
809 -class Decoder(pyamf.BaseDecoder):
810 """ 811 Decodes an AMF3 data stream. 812 """ 813 context_class = Context 814 815 type_map = { 816 TYPE_UNDEFINED: 'readUndefined', 817 TYPE_NULL: 'readNull', 818 TYPE_BOOL_FALSE: 'readBoolFalse', 819 TYPE_BOOL_TRUE: 'readBoolTrue', 820 TYPE_INTEGER: 'readSignedInteger', 821 TYPE_NUMBER: 'readNumber', 822 TYPE_STRING: 'readString', 823 TYPE_XML: 'readXML', 824 TYPE_DATE: 'readDate', 825 TYPE_ARRAY: 'readArray', 826 TYPE_OBJECT: 'readObject', 827 TYPE_XMLSTRING: 'readXMLString', 828 TYPE_BYTEARRAY: 'readByteArray', 829 } 830
831 - def __init__(self, *args, **kwargs):
832 self.use_proxies = kwargs.pop('use_proxies', use_proxies_default) 833 834 pyamf.BaseDecoder.__init__(self, *args, **kwargs)
835
836 - def readUndefined(self):
837 """ 838 Read undefined. 839 """ 840 return pyamf.Undefined
841
842 - def readNull(self):
843 """ 844 Read null. 845 846 @return: C{None} 847 @rtype: C{None} 848 """ 849 return None
850
851 - def readBoolFalse(self):
852 """ 853 Returns C{False}. 854 855 @return: C{False} 856 @rtype: C{bool} 857 """ 858 return False
859
860 - def readBoolTrue(self):
861 """ 862 Returns C{True}. 863 864 @return: C{True} 865 @rtype: C{bool} 866 """ 867 return True
868
869 - def readNumber(self):
870 """ 871 Read number. 872 """ 873 return self.stream.read_double()
874
875 - def readUnsignedInteger(self):
876 """ 877 Reads and returns an unsigned integer from the stream. 878 """ 879 return self.readInteger(False)
880
881 - def readSignedInteger(self):
882 """ 883 Reads and returns a signed integer from the stream. 884 """ 885 return self.readInteger(True)
886
887 - def readInteger(self, signed=False):
888 """ 889 Reads and returns an integer from the stream. 890 891 @type signed: C{bool} 892 @see: U{Parsing integers on OSFlash 893 <http://osflash.org/amf3/parsing_integers>} for the AMF3 integer data 894 format. 895 """ 896 return decode_int(self.stream, signed)
897
898 - def readString(self, use_references=True):
899 """ 900 Reads and returns a string from the stream. 901 902 @type use_references: C{bool} 903 """ 904 def readLength(): 905 x = self.readUnsignedInteger() 906 907 return (x >> 1, x & REFERENCE_BIT == 0)
908 909 length, is_reference = readLength() 910 911 if use_references and is_reference: 912 return self.context.getString(length) 913 914 if length == 0: 915 return u'' 916 917 result = self.stream.read_utf8_string(length) 918 919 if len(result) != 0 and use_references: 920 self.context.addString(result) 921 922 return result
923
924 - def readDate(self):
925 """ 926 Read date from the stream. 927 928 The timezone is ignored as the date is always in UTC. 929 """ 930 ref = self.readUnsignedInteger() 931 932 if ref & REFERENCE_BIT == 0: 933 return self.context.getObject(ref >> 1) 934 935 ms = self.stream.read_double() 936 result = util.get_datetime(ms / 1000.0) 937 938 if self.timezone_offset is not None: 939 result += self.timezone_offset 940 941 self.context.addObject(result) 942 943 return result
944
945 - def readArray(self):
946 """ 947 Reads an array from the stream. 948 949 @warning: There is a very specific problem with AMF3 where the first 950 three bytes of an encoded empty C{dict} will mirror that of an encoded 951 C{{'': 1, '2': 2}} 952 953 @see: U{Docuverse blog (external) 954 <http://www.docuverse.com/blog/donpark/2007/05/14/flash-9-amf3-bug>} 955 """ 956 size = self.readUnsignedInteger() 957 958 if size & REFERENCE_BIT == 0: 959 return self.context.getObject(size >> 1) 960 961 size >>= 1 962 963 key = self.readString().encode('utf8') 964 965 if key == '': 966 # integer indexes only -> python list 967 result = [] 968 self.context.addObject(result) 969 970 for i in xrange(size): 971 result.append(self.readElement()) 972 973 return result 974 975 result = pyamf.MixedArray() 976 self.context.addObject(result) 977 978 while key != "": 979 result[key] = self.readElement() 980 key = self.readString().encode('utf8') 981 982 for i in xrange(size): 983 el = self.readElement() 984 result[i] = el 985 986 return result
987
988 - def _getClassDefinition(self, ref):
989 """ 990 Reads class definition from the stream. 991 """ 992 is_ref = ref & REFERENCE_BIT == 0 993 ref >>= 1 994 995 if is_ref: 996 class_def = self.context.getClassByReference(ref) 997 998 return class_def, class_def.alias 999 1000 name = self.readString() 1001 alias = None 1002 1003 if name == '': 1004 name = pyamf.ASObject 1005 1006 try: 1007 alias = pyamf.get_class_alias(name) 1008 except pyamf.UnknownClassAlias: 1009 if self.strict: 1010 raise 1011 1012 alias = pyamf.TypedObjectClassAlias(pyamf.TypedObject, name) 1013 1014 class_def = ClassDefinition(alias) 1015 1016 class_def.encoding = ref & 0x03 1017 class_def.attr_len = ref >> 2 1018 class_def.static_properties = [] 1019 1020 if class_def.attr_len > 0: 1021 for i in xrange(class_def.attr_len): 1022 key = self.readString().encode('utf8') 1023 1024 class_def.static_properties.append(key) 1025 1026 self.context.addClass(class_def, alias.klass) 1027 1028 return class_def, alias
1029
1030 - def readObject(self, use_proxies=None):
1031 """ 1032 Reads an object from the stream. 1033 1034 @raise pyamf.EncodeError: Decoding an object in amf3 tagged as amf0 1035 only is not allowed. 1036 @raise pyamf.DecodeError: Unknown object encoding. 1037 """ 1038 if use_proxies is None: 1039 use_proxies = self.use_proxies 1040 1041 def readStatic(class_def, obj): 1042 for attr in class_def.static_properties: 1043 obj[attr] = self.readElement()
1044 1045 def readDynamic(class_def, obj): 1046 attr = self.readString().encode('utf8') 1047 1048 while attr != '': 1049 obj[attr] = self.readElement() 1050 attr = self.readString().encode('utf8') 1051 1052 ref = self.readUnsignedInteger() 1053 1054 if ref & REFERENCE_BIT == 0: 1055 obj = self.context.getObject(ref >> 1) 1056 1057 if use_proxies is True: 1058 obj = self.readProxyObject(obj) 1059 1060 return obj 1061 1062 ref >>= 1 1063 1064 class_def, alias = self._getClassDefinition(ref) 1065 1066 obj = alias.createInstance(codec=self) 1067 obj_attrs = dict() 1068 1069 self.context.addObject(obj) 1070 1071 if class_def.encoding in (ObjectEncoding.EXTERNAL, ObjectEncoding.PROXY): 1072 obj.__readamf__(DataInput(self)) 1073 elif class_def.encoding == ObjectEncoding.DYNAMIC: 1074 readStatic(class_def, obj_attrs) 1075 readDynamic(class_def, obj_attrs) 1076 elif class_def.encoding == ObjectEncoding.STATIC: 1077 readStatic(class_def, obj_attrs) 1078 else: 1079 raise pyamf.DecodeError("Unknown object encoding") 1080 1081 alias.applyAttributes(obj, obj_attrs, codec=self) 1082 1083 if use_proxies is True: 1084 obj = self.readProxyObject(obj) 1085 1086 return obj 1087
1088 - def readProxyObject(self, proxy):
1089 """ 1090 Return the source object of a proxied object. 1091 1092 @since: 0.4 1093 """ 1094 if isinstance(proxy, ArrayCollection): 1095 return list(proxy) 1096 elif isinstance(proxy, ObjectProxy): 1097 return proxy._amf_object 1098 1099 return proxy
1100
1101 - def _readXML(self, legacy=False):
1102 """ 1103 Reads an object from the stream. 1104 1105 @type legacy: C{bool} 1106 @param legacy: The read XML is in 'legacy' format. 1107 """ 1108 ref = self.readUnsignedInteger() 1109 1110 if ref & REFERENCE_BIT == 0: 1111 return self.context.getObject(ref >> 1) 1112 1113 xmlstring = self.stream.read(ref >> 1) 1114 1115 x = util.ET.fromstring(xmlstring) 1116 self.context.addObject(x) 1117 1118 if legacy is True: 1119 self.context.addLegacyXML(x) 1120 1121 return x
1122
1123 - def readXMLString(self):
1124 """ 1125 Reads a string from the data stream and converts it into 1126 an XML Tree. 1127 1128 @return: The XML Document. 1129 @rtype: L{ET<util.ET>} 1130 """ 1131 return self._readXML()
1132
1133 - def readXML(self):
1134 """ 1135 Read a legacy XML Document from the stream. 1136 1137 @return: The XML Document. 1138 @rtype: L{ET<util.ET>} 1139 """ 1140 return self._readXML(True)
1141
1142 - def readByteArray(self):
1143 """ 1144 Reads a string of data from the stream. 1145 1146 Detects if the L{ByteArray} was compressed using C{zlib}. 1147 1148 @see: L{ByteArray} 1149 @note: This is not supported in ActionScript 1.0 and 2.0. 1150 """ 1151 ref = self.readUnsignedInteger() 1152 1153 if ref & REFERENCE_BIT == 0: 1154 return self.context.getObject(ref >> 1) 1155 1156 buffer = self.stream.read(ref >> 1) 1157 1158 try: 1159 buffer = zlib.decompress(buffer) 1160 compressed = True 1161 except zlib.error: 1162 compressed = False 1163 1164 obj = ByteArray(buffer, context=self.context) 1165 obj.compressed = compressed 1166 1167 self.context.addObject(obj) 1168 1169 return obj
1170 1171
1172 -class Encoder(pyamf.BaseEncoder):
1173 """ 1174 Encodes an AMF3 data stream. 1175 """ 1176 context_class = Context 1177 1178 type_map = [ 1179 ((types.BuiltinFunctionType, types.BuiltinMethodType, 1180 types.FunctionType, types.GeneratorType, types.ModuleType, 1181 types.LambdaType, types.MethodType), "writeFunc"), 1182 ((bool,), "writeBoolean"), 1183 ((types.NoneType,), "writeNull"), 1184 ((int,long), "writeInteger"), 1185 ((float,), "writeNumber"), 1186 (types.StringTypes, "writeString"), 1187 ((ByteArray,), "writeByteArray"), 1188 ((datetime.date, datetime.datetime, datetime.time), "writeDate"), 1189 ((util.is_ET_element,), "writeXML"), 1190 ((pyamf.UndefinedType,), "writeUndefined"), 1191 ((types.ClassType, types.TypeType), "writeClass"), 1192 ((types.InstanceType, types.ObjectType,), "writeInstance"), 1193 ] 1194
1195 - def __init__(self, *args, **kwargs):
1196 self.use_proxies = kwargs.pop('use_proxies', use_proxies_default) 1197 self.string_references = kwargs.pop('string_references', True) 1198 1199 pyamf.BaseEncoder.__init__(self, *args, **kwargs)
1200
1201 - def writeElement(self, data, use_references=True, use_proxies=None):
1202 """ 1203 Writes the data. 1204 1205 @param data: The data to be encoded to the AMF3 data stream. 1206 @type data: C{mixed} 1207 @param use_references: Default is C{True}. 1208 @type use_references: C{bool} 1209 @raise EncodeError: Cannot find encoder func for C{data}. 1210 """ 1211 func = self._writeElementFunc(data) 1212 1213 if func is None: 1214 raise pyamf.EncodeError("Unknown type %r" % (data,)) 1215 1216 func(data, use_references=use_references, use_proxies=use_proxies)
1217
1218 - def writeClass(self, *args, **kwargs):
1219 """ 1220 Classes cannot be serialised. 1221 """ 1222 raise pyamf.EncodeError("Class objects cannot be serialised")
1223
1224 - def writeUndefined(self, *args, **kwargs):
1225 """ 1226 Writes an C{pyamf.Undefined} value to the stream. 1227 """ 1228 self.stream.write(TYPE_UNDEFINED)
1229
1230 - def writeNull(self, *args, **kwargs):
1231 """ 1232 Writes a C{null} value to the stream. 1233 """ 1234 self.stream.write(TYPE_NULL)
1235
1236 - def writeBoolean(self, n, **kwargs):
1237 """ 1238 Writes a Boolean to the stream. 1239 """ 1240 t = TYPE_BOOL_TRUE 1241 1242 if not n: 1243 t = TYPE_BOOL_FALSE 1244 1245 self.stream.write(t)
1246
1247 - def _writeInteger(self, n):
1248 """ 1249 AMF3 integers are encoded. 1250 1251 @param n: The integer data to be encoded to the AMF3 data stream. 1252 @type n: integer data 1253 1254 @see: U{Parsing Integers on OSFlash 1255 <http://osflash.org/documentation/amf3/parsing_integers>} 1256 for more info. 1257 """ 1258 try: 1259 self.stream.write(ENCODED_INT_CACHE[n]) 1260 except KeyError: 1261 ENCODED_INT_CACHE[n] = encode_int(n) 1262 self.stream.write(ENCODED_INT_CACHE[n])
1263
1264 - def writeInteger(self, n, **kwargs):
1265 """ 1266 Writes an integer to the stream. 1267 1268 @type n: integer data 1269 @param n: The integer data to be encoded to the AMF3 data stream. 1270 @type use_references: C{bool} 1271 @kwarg use_references: Default is C{True}. 1272 """ 1273 if n < 0 or n > MAX_29B_INT: 1274 self.writeNumber(float(n)) 1275 1276 return 1277 1278 self.stream.write(TYPE_INTEGER) 1279 self.stream.write(encode_int(n))
1280
1281 - def writeNumber(self, n, **kwargs):
1282 """ 1283 Writes a float to the stream. 1284 1285 @type n: C{float} 1286 """ 1287 self.stream.write(TYPE_NUMBER) 1288 self.stream.write_double(n)
1289
1290 - def _writeString(self, n, **kwargs):
1291 """ 1292 Writes a raw string to the stream. 1293 1294 @type n: C{str} or C{unicode} 1295 @param n: The string data to be encoded to the AMF3 data stream. 1296 """ 1297 if n == '': 1298 self.stream.write_uchar(REFERENCE_BIT) 1299 1300 return 1301 1302 t = type(n) 1303 1304 if t is str: 1305 bytes = n 1306 elif t is unicode: 1307 bytes = n.encode('utf8') 1308 else: 1309 bytes = unicode(n).encode('utf8') 1310 n = bytes 1311 1312 if self.string_references: 1313 ref = self.context.getStringReference(n) 1314 1315 if ref is not None: 1316 self._writeInteger(ref << 1) 1317 1318 return 1319 1320 self.context.addString(n) 1321 1322 self._writeInteger((len(bytes) << 1) | REFERENCE_BIT) 1323 self.stream.write(bytes)
1324
1325 - def writeString(self, n, **kwargs):
1326 """ 1327 Writes a string to the stream. If C{n} is not a unicode string, an 1328 attempt will be made to convert it. 1329 1330 @type n: C{basestring} 1331 @param n: The string data to be encoded to the AMF3 data stream. 1332 @type use_references: C{bool} 1333 @kwarg use_references: Default is C{True}. 1334 """ 1335 self.stream.write(TYPE_STRING) 1336 1337 self._writeString(n, **kwargs)
1338
1339 - def writeDate(self, n, use_references=True, **kwargs):
1340 """ 1341 Writes a C{datetime} instance to the stream. 1342 1343 @type n: L{datetime} 1344 @param n: The C{Date} data to be encoded to the AMF3 data stream. 1345 @type use_references: C{bool} 1346 @param use_references: Default is C{True}. 1347 """ 1348 if isinstance(n, datetime.time): 1349 raise pyamf.EncodeError('A datetime.time instance was found but ' 1350 'AMF3 has no way to encode time objects. Please use ' 1351 'datetime.datetime instead (got:%r)' % (n,)) 1352 1353 self.stream.write(TYPE_DATE) 1354 1355 if use_references is True: 1356 ref = self.context.getObjectReference(n) 1357 1358 if ref is not None: 1359 self._writeInteger(ref << 1) 1360 1361 return 1362 1363 self.context.addObject(n) 1364 1365 self.stream.write_uchar(REFERENCE_BIT) 1366 1367 if self.timezone_offset is not None: 1368 n -= self.timezone_offset 1369 1370 ms = util.get_timestamp(n) 1371 self.stream.write_double(ms * 1000.0)
1372
1373 - def writeList(self, n, use_references=True, use_proxies=None):
1374 """ 1375 Writes a C{tuple}, C{set} or C{list} to the stream. 1376 1377 @type n: One of C{__builtin__.tuple}, C{__builtin__.set} 1378 or C{__builtin__.list} 1379 @param n: The C{list} data to be encoded to the AMF3 data stream. 1380 @type use_references: C{bool} 1381 @param use_references: Default is C{True}. 1382 """ 1383 # Encode lists as ArrayCollections 1384 if use_proxies is None: 1385 use_proxies = self.use_proxies 1386 1387 if use_proxies: 1388 ref_obj = self.context.getObjectAlias(n) 1389 1390 if ref_obj is None: 1391 proxy = ArrayCollection(n) 1392 self.context.setObjectAlias(n, proxy) 1393 ref_obj = proxy 1394 1395 self.writeObject(ref_obj, use_references, use_proxies=False) 1396 1397 return 1398 1399 self.stream.write(TYPE_ARRAY) 1400 1401 if use_references: 1402 ref = self.context.getObjectReference(n) 1403 1404 if ref is not None: 1405 self._writeInteger(ref << 1) 1406 1407 return 1408 1409 self.context.addObject(n) 1410 1411 self._writeInteger((len(n) << 1) | REFERENCE_BIT) 1412 self.stream.write_uchar(0x01) 1413 1414 [self.writeElement(x) for x in n]
1415
1416 - def writeDict(self, n, use_references=True, use_proxies=None):
1417 """ 1418 Writes a C{dict} to the stream. 1419 1420 @type n: C{__builtin__.dict} 1421 @param n: The C{dict} data to be encoded to the AMF3 data stream. 1422 @type use_references: C{bool} 1423 @param use_references: Default is C{True}. 1424 @raise ValueError: Non C{int}/C{str} key value found in the C{dict} 1425 @raise EncodeError: C{dict} contains empty string keys. 1426 """ 1427 1428 # Design bug in AMF3 that cannot read/write empty key strings 1429 # http://www.docuverse.com/blog/donpark/2007/05/14/flash-9-amf3-bug 1430 # for more info 1431 if '' in n: 1432 raise pyamf.EncodeError("dicts cannot contain empty string keys") 1433 1434 if use_proxies is None: 1435 use_proxies = self.use_proxies 1436 1437 if use_proxies is True: 1438 ref_obj = self.context.getObjectAlias(n) 1439 1440 if ref_obj is None: 1441 proxy = ObjectProxy(pyamf.ASObject(n)) 1442 self.context.setObjectAlias(n, proxy) 1443 ref_obj = proxy 1444 1445 self.writeObject(ref_obj, use_references, use_proxies=False) 1446 1447 return 1448 1449 self.stream.write(TYPE_ARRAY) 1450 1451 if use_references: 1452 ref = self.context.getObjectReference(n) 1453 1454 if ref is not None: 1455 self._writeInteger(ref << 1) 1456 1457 return 1458 1459 self.context.addObject(n) 1460 1461 # The AMF3 spec demands that all str based indicies be listed first 1462 keys = n.keys() 1463 int_keys = [] 1464 str_keys = [] 1465 1466 for x in keys: 1467 if isinstance(x, (int, long)): 1468 int_keys.append(x) 1469 elif isinstance(x, (str, unicode)): 1470 str_keys.append(x) 1471 else: 1472 raise ValueError("Non int/str key value found in dict") 1473 1474 # Make sure the integer keys are within range 1475 l = len(int_keys) 1476 1477 for x in int_keys: 1478 if l < x <= 0: 1479 # treat as a string key 1480 str_keys.append(x) 1481 del int_keys[int_keys.index(x)] 1482 1483 int_keys.sort() 1484 1485 # If integer keys don't start at 0, they will be treated as strings 1486 if len(int_keys) > 0 and int_keys[0] != 0: 1487 for x in int_keys: 1488 str_keys.append(str(x)) 1489 del int_keys[int_keys.index(x)] 1490 1491 self._writeInteger(len(int_keys) << 1 | REFERENCE_BIT) 1492 1493 for x in str_keys: 1494 self._writeString(x) 1495 self.writeElement(n[x]) 1496 1497 self.stream.write_uchar(0x01) 1498 1499 for k in int_keys: 1500 self.writeElement(n[k])
1501
1502 - def writeInstance(self, obj, **kwargs):
1503 """ 1504 Read class definition. 1505 1506 @param obj: The class instance to be encoded. 1507 """ 1508 kls = obj.__class__ 1509 1510 if kls is pyamf.MixedArray: 1511 f = self._write_elem_func_cache[kls] = self.writeDict 1512 elif kls in (list, set, tuple): 1513 f = self._write_elem_func_cache[kls] = self.writeList 1514 else: 1515 f = self._write_elem_func_cache[kls] = self.writeObject 1516 1517 f(obj, **kwargs)
1518
1519 - def writeObject(self, obj, use_references=True, use_proxies=None):
1520 """ 1521 Writes an object to the stream. 1522 1523 @param obj: The object data to be encoded to the AMF3 data stream. 1524 @type obj: object data 1525 @param use_references: Default is C{True}. 1526 @type use_references: C{bool} 1527 @raise EncodeError: Encoding an object in amf3 tagged as amf0 only. 1528 """ 1529 if use_proxies is None: 1530 use_proxies = self.use_proxies 1531 1532 if use_proxies is True and obj.__class__ is dict: 1533 ref_obj = self.context.getObjectAlias(obj) 1534 1535 if ref_obj is None: 1536 proxy = ObjectProxy(obj) 1537 self.context.setObjectAlias(obj, proxy) 1538 ref_obj = proxy 1539 1540 self.writeObject(ref_obj, use_references, use_proxies=False) 1541 1542 return 1543 1544 self.stream.write(TYPE_OBJECT) 1545 1546 if use_references: 1547 ref = self.context.getObjectReference(obj) 1548 1549 if ref is not None: 1550 self._writeInteger(ref << 1) 1551 1552 return 1553 1554 self.context.addObject(obj) 1555 1556 # object is not referenced, serialise it 1557 kls = obj.__class__ 1558 definition = self.context.getClass(kls) 1559 alias = None 1560 class_ref = False # if the class definition is a reference 1561 1562 if definition: 1563 class_ref = True 1564 alias = definition.alias 1565 1566 if alias.anonymous and definition.reference is not None: 1567 class_ref = True 1568 else: 1569 try: 1570 alias = pyamf.get_class_alias(kls) 1571 except pyamf.UnknownClassAlias: 1572 alias_klass = util.get_class_alias(kls) 1573 1574 alias = alias_klass(kls, defer=True) 1575 1576 definition = ClassDefinition(alias) 1577 1578 self.context.addClass(definition, alias.klass) 1579 1580 if class_ref: 1581 self.stream.write(definition.reference) 1582 1583 if alias.anonymous: 1584 self.stream.write_uchar(0x01) 1585 else: 1586 ref = 0 1587 1588 if definition.encoding != ObjectEncoding.EXTERNAL: 1589 ref += definition.attr_len << 4 1590 1591 final_reference = encode_int(ref | definition.encoding << 2 | 1592 REFERENCE_BIT << 1 | REFERENCE_BIT) 1593 1594 self.stream.write(final_reference) 1595 1596 if alias.anonymous: 1597 self.stream.write_uchar(0x01) 1598 #self._writeString('') 1599 else: 1600 self._writeString(alias.alias) 1601 1602 # work out what the final reference for the class will be. 1603 # this is okay because the next time an object of the same 1604 # class is encoded, class_ref will be True and never get here 1605 # again. 1606 if alias.anonymous: 1607 definition.reference = final_reference 1608 else: 1609 definition.reference = encode_int( 1610 definition.reference << 2 | REFERENCE_BIT) 1611 1612 if alias.external: 1613 obj.__writeamf__(DataOutput(self)) 1614 1615 return 1616 1617 sa, da = alias.getEncodableAttributes(obj, codec=self) 1618 1619 if sa: 1620 if not class_ref: 1621 [self._writeString(attr) for attr in alias.static_attrs] 1622 1623 [self.writeElement(sa[attr]) for attr in alias.static_attrs] 1624 1625 if definition.encoding == ObjectEncoding.STATIC: 1626 return 1627 1628 if definition.encoding == ObjectEncoding.DYNAMIC: 1629 if da: 1630 for attr, value in da.iteritems(): 1631 self._writeString(attr) 1632 self.writeElement(value) 1633 1634 self.stream.write_uchar(0x01)
1635
1636 - def writeByteArray(self, n, use_references=True, **kwargs):
1637 """ 1638 Writes a L{ByteArray} to the data stream. 1639 1640 @param n: The L{ByteArray} data to be encoded to the AMF3 data stream. 1641 @type n: L{ByteArray} 1642 @param use_references: Default is C{True}. 1643 @type use_references: C{bool} 1644 """ 1645 self.stream.write(TYPE_BYTEARRAY) 1646 1647 if use_references: 1648 ref = self.context.getObjectReference(n) 1649 1650 if ref is not None: 1651 self._writeInteger(ref << 1) 1652 1653 return 1654 1655 self.context.addObject(n) 1656 1657 buf = str(n) 1658 l = len(buf) 1659 self._writeInteger(l << 1 | REFERENCE_BIT) 1660 self.stream.write(buf)
1661
1662 - def writeXML(self, n, use_references=True, use_proxies=None):
1663 """ 1664 Writes a XML string to the data stream. 1665 1666 @type n: L{ET<util.ET>} 1667 @param n: The XML Document to be encoded to the AMF3 data stream. 1668 @type use_references: C{bool} 1669 @param use_references: Default is C{True}. 1670 """ 1671 i = self.context.getLegacyXMLReference(n) 1672 1673 if i is None: 1674 is_legacy = True 1675 else: 1676 is_legacy = False 1677 1678 if is_legacy is True: 1679 self.stream.write(TYPE_XMLSTRING) 1680 else: 1681 self.stream.write(TYPE_XML) 1682 1683 if use_references: 1684 ref = self.context.getObjectReference(n) 1685 1686 if ref is not None: 1687 self._writeInteger(ref << 1) 1688 1689 return 1690 1691 self.context.addObject(n) 1692 1693 self._writeString(util.ET.tostring(n, 'utf-8'))
1694 1695
1696 -def decode(stream, context=None, strict=False):
1697 """ 1698 A helper function to decode an AMF3 datastream. 1699 1700 @type stream: L{BufferedByteStream<util.BufferedByteStream>} 1701 @param stream: AMF3 data. 1702 @type context: L{Context} 1703 @param context: Context. 1704 """ 1705 decoder = Decoder(stream, context, strict) 1706 1707 while 1: 1708 try: 1709 yield decoder.readElement() 1710 except pyamf.EOStream: 1711 break
1712 1713
1714 -def encode(*args, **kwargs):
1715 """ 1716 A helper function to encode an element into AMF3 format. 1717 1718 @type args: List of args to encode. 1719 @keyword context: Any initial context to use. 1720 @type context: L{Context} 1721 @return: C{StringIO} type object containing the encoded AMF3 data. 1722 @rtype: L{BufferedByteStream<pyamf.util.BufferedByteStream>} 1723 """ 1724 context = kwargs.get('context', None) 1725 buf = util.BufferedByteStream() 1726 encoder = Encoder(buf, context) 1727 1728 for element in args: 1729 encoder.writeElement(element) 1730 1731 return buf
1732 1733
1734 -def encode_int(n):
1735 """ 1736 Encodes an int as a variable length unsigned 29-bit integer as defined by 1737 the spec. 1738 1739 @param n: The integer to be encoded 1740 @return: The encoded string 1741 @rtype: C{str} 1742 @raise OverflowError: Out of range. 1743 """ 1744 if n < 0 or n > MAX_29B_INT: 1745 raise OverflowError("Out of range") 1746 1747 bytes = '' 1748 real_value = None 1749 1750 if n > 0x1fffff: 1751 real_value = n 1752 n >>= 1 1753 bytes += chr(0x80 | ((n >> 21) & 0xff)) 1754 1755 if n > 0x3fff: 1756 bytes += chr(0x80 | ((n >> 14) & 0xff)) 1757 1758 if n > 0x7f: 1759 bytes += chr(0x80 | ((n >> 7) & 0xff)) 1760 1761 if real_value is not None: 1762 n = real_value 1763 1764 if n > 0x1fffff: 1765 bytes += chr(n & 0xff) 1766 else: 1767 bytes += chr(n & 0x7f) 1768 1769 return bytes
1770 1771
1772 -def decode_int(stream, signed=False):
1773 """ 1774 Decode C{int}. 1775 """ 1776 n = result = 0 1777 b = stream.read_uchar() 1778 1779 while b & 0x80 != 0 and n < 3: 1780 result <<= 7 1781 result |= b & 0x7f 1782 b = stream.read_uchar() 1783 n += 1 1784 1785 if n < 3: 1786 result <<= 7 1787 result |= b 1788 else: 1789 result <<= 8 1790 result |= b 1791 1792 if result & 0x10000000 != 0: 1793 if signed: 1794 result -= 0x20000000 1795 else: 1796 result <<= 1 1797 result += 1 1798 1799 return result
1800 1801 try: 1802 from cpyamf.amf3 import encode_int, decode_int 1803 except ImportError: 1804 pass 1805 1806 1807 pyamf.register_class(ByteArray) 1808 1809 for x in range(0, 20): 1810 ENCODED_INT_CACHE[x] = encode_int(x) 1811 del x 1812