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