Package pyffi :: Package object_models :: Module common
[hide private]
[frames] | no frames]

Source Code for Module pyffi.object_models.common

  1  """Implements common basic types in XML file format descriptions.""" 
  2   
  3  # ***** BEGIN LICENSE BLOCK ***** 
  4  # 
  5  # Copyright (c) 2007-2011, Python File Format Interface. 
  6  # All rights reserved. 
  7  # 
  8  # Redistribution and use in source and binary forms, with or without 
  9  # modification, are permitted provided that the following conditions 
 10  # are met: 
 11  # 
 12  #    * Redistributions of source code must retain the above copyright 
 13  #      notice, this list of conditions and the following disclaimer. 
 14  # 
 15  #    * Redistributions in binary form must reproduce the above 
 16  #      copyright notice, this list of conditions and the following 
 17  #      disclaimer in the documentation and/or other materials provided 
 18  #      with the distribution. 
 19  # 
 20  #    * Neither the name of the Python File Format Interface 
 21  #      project nor the names of its contributors may be used to endorse 
 22  #      or promote products derived from this software without specific 
 23  #      prior written permission. 
 24  # 
 25  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 26  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 27  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 28  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 29  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 30  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 31  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 32  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 33  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 34  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 35  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 36  # POSSIBILITY OF SUCH DAMAGE. 
 37  # 
 38  # ***** END LICENSE BLOCK ***** 
 39   
 40  import struct 
 41  import logging 
 42   
 43  from pyffi.object_models.xml.basic import BasicBase 
 44  from pyffi.object_models.editable import EditableSpinBox 
 45  from pyffi.object_models.editable import EditableFloatSpinBox 
 46  from pyffi.object_models.editable import EditableLineEdit 
 47  from pyffi.object_models.editable import EditableBoolComboBox 
 48   
 49  _b = "".encode("ascii") # py3k's b"" 
 50  _b00 = "\x00".encode("ascii") # py3k's b"\x00" 
 51   
 52  # supports the bytes object for < py26 
 53  try: 
 54      bytes 
 55  except NameError: 
 56      bytes = str # for py25 backwards compatibility 
 57   
 58  if bytes is str: 
 59      # < py3k: str for byte strings, unicode for text strings 
 60      _bytes = str 
 61      _str = unicode 
 62  else: 
 63      # >= py3k: bytes for byte strings, str for text strings 
 64      _bytes = bytes 
 65      _str = str 
66 67 -def _as_bytes(value):
68 """Helper function which converts a string to _bytes (this is useful for 69 set_value in all string classes, which use bytes for representation). 70 71 :return: The bytes representing the value. 72 :rtype: C{_bytes} 73 74 >>> # following doctest fails on py3k, hence disabled 75 >>> _as_bytes(u"\\u00e9defa") == u"\\u00e9defa".encode("utf-8") # doctest: +SKIP 76 True 77 78 >>> _as_bytes(123) # doctest: +ELLIPSIS 79 Traceback (most recent call last): 80 ... 81 TypeError: ... 82 """ 83 if isinstance(value, _bytes): 84 return value 85 elif isinstance(value, _str): 86 return value.encode("utf-8", "replace") 87 else: 88 raise TypeError("expected %s or %s" % (_bytes.__name__, _str.__name__))
89
90 -def _as_str(value):
91 """Helper function to convert bytes back to str. This is used in 92 the __str__ functions for simple string types. If you want a custom 93 encoding, use an explicit decode call on the value. 94 """ 95 if not isinstance(value, (_str, _bytes)): 96 raise TypeError("expected %s or %s" % (_bytes.__name__, _str.__name__)) 97 elif not value: 98 return '' 99 elif isinstance(value, str): 100 # this always works regardless of the python version 101 return value 102 elif isinstance(value, _bytes): 103 # >= py3k: simply decode 104 return value.decode("utf-8", "replace") 105 elif isinstance(value, unicode): 106 # < py3k: use ascii encoding to produce a str 107 # (this avoids unicode errors) 108 return value.encode("ascii", "replace")
109
110 -class Int(BasicBase, EditableSpinBox):
111 """Basic implementation of a 32-bit signed integer type. Also serves as a 112 base class for all other integer types. Follows specified byte order. 113 114 >>> from tempfile import TemporaryFile 115 >>> tmp = TemporaryFile() 116 >>> from pyffi.object_models import FileFormat 117 >>> data = FileFormat.Data() 118 >>> i = Int() 119 >>> i.set_value(-1) 120 >>> i.get_value() 121 -1 122 >>> i.set_value(0x11223344) 123 >>> i.write(tmp, data) 124 >>> j = Int() 125 >>> if tmp.seek(0): pass # ignore result for py3k 126 >>> j.read(tmp, data) 127 >>> hex(j.get_value()) 128 '0x11223344' 129 >>> i.set_value(2**40) # doctest: +ELLIPSIS 130 Traceback (most recent call last): 131 ... 132 ValueError: ... 133 >>> i.set_value('hello world') 134 Traceback (most recent call last): 135 ... 136 ValueError: cannot convert value 'hello world' to integer 137 >>> if tmp.seek(0): pass # ignore result for py3k 138 >>> if tmp.write('\x11\x22\x33\x44'.encode("ascii")): pass # b'\x11\x22\x33\x44' 139 >>> if tmp.seek(0): pass # ignore result for py3k 140 >>> i.read(tmp, data) 141 >>> hex(i.get_value()) 142 '0x44332211' 143 """ 144 145 _min = -0x80000000 #: Minimum value. 146 _max = 0x7fffffff #: Maximum value. 147 _struct = 'i' #: Character used to represent type in struct. 148 _size = 4 #: Number of bytes. 149
150 - def __init__(self, **kwargs):
151 """Initialize the integer.""" 152 super(Int, self).__init__(**kwargs) 153 self._value = 0
154
155 - def get_value(self):
156 """Return stored value. 157 158 :return: The stored value. 159 """ 160 return self._value
161
162 - def set_value(self, value):
163 """Set value to C{value}. Calls C{int(value)} to convert to integer. 164 165 :param value: The value to assign. 166 :type value: int 167 """ 168 try: 169 val = int(value) 170 except ValueError: 171 try: 172 val = int(value, 16) # for '0x...' strings 173 except ValueError: 174 try: 175 val = getattr(self, value) # for enums 176 except AttributeError: 177 raise ValueError( 178 "cannot convert value '%s' to integer"%value) 179 if val < self._min or val > self._max: 180 raise ValueError('value out of range (%i)' % val) 181 self._value = val
182
183 - def read(self, stream, data):
184 """Read value from stream. 185 186 :param stream: The stream to read from. 187 :type stream: file 188 """ 189 self._value = struct.unpack(data._byte_order + self._struct, 190 stream.read(self._size))[0]
191
192 - def write(self, stream, data):
193 """Write value to stream. 194 195 :param stream: The stream to write to. 196 :type stream: file 197 """ 198 stream.write(struct.pack(data._byte_order + self._struct, self._value))
199
200 - def __str__(self):
201 return str(self.get_value())
202 203 @classmethod
204 - def get_size(cls, data=None):
205 """Return number of bytes this type occupies in a file. 206 207 :return: Number of bytes. 208 """ 209 return cls._size
210
211 - def get_hash(self, data=None):
212 """Return a hash value for this value. 213 214 :return: An immutable object that can be used as a hash. 215 """ 216 return self.get_value()
217
218 - def get_editor_minimum(self):
219 """Minimum possible value. 220 221 :return: Minimum possible value. 222 """ 223 return self._min
224
225 - def get_editor_maximum(self):
226 """Maximum possible value. 227 228 :return: Maximum possible value. 229 """ 230 return self._max
231
232 -class UInt(Int):
233 """Implementation of a 32-bit unsigned integer type.""" 234 _min = 0 235 _max = 0xffffffff 236 _struct = 'I' 237 _size = 4
238
239 -class Int64(Int):
240 """Implementation of a 64-bit signed integer type.""" 241 _min = -0x8000000000000000 242 _max = 0x7fffffffffffffff 243 _struct = 'q' 244 _size = 8
245
246 -class UInt64(Int):
247 """Implementation of a 64-bit unsigned integer type.""" 248 _min = 0 249 _max = 0xffffffffffffffff 250 _struct = 'Q' 251 _size = 8
252
253 -class Byte(Int):
254 """Implementation of a 8-bit signed integer type.""" 255 _min = -0x80 256 _max = 0x7f 257 _struct = 'b' 258 _size = 1
259
260 -class UByte(Int):
261 """Implementation of a 8-bit unsigned integer type.""" 262 _min = 0 263 _max = 0xff 264 _struct = 'B' 265 _size = 1
266
267 -class Short(Int):
268 """Implementation of a 16-bit signed integer type.""" 269 _min = -0x8000 270 _max = 0x7fff 271 _struct = 'h' 272 _size = 2
273
274 -class UShort(UInt):
275 """Implementation of a 16-bit unsigned integer type.""" 276 _min = 0 277 _max = 0xffff 278 _struct = 'H' 279 _size = 2
280
281 -class ULittle32(UInt):
282 """Little endian 32 bit unsigned integer (ignores specified data 283 byte order). 284 """
285 - def read(self, stream, data):
286 """Read value from stream. 287 288 :param stream: The stream to read from. 289 :type stream: file 290 """ 291 self._value = struct.unpack('<' + self._struct, 292 stream.read(self._size))[0]
293
294 - def write(self, stream, data):
295 """Write value to stream. 296 297 :param stream: The stream to write to. 298 :type stream: file 299 """ 300 stream.write(struct.pack('<' + self._struct, self._value))
301
302 -class Bool(UByte, EditableBoolComboBox):
303 """Simple bool implementation.""" 304
305 - def get_value(self):
306 """Return stored value. 307 308 :return: The stored value. 309 """ 310 return bool(self._value)
311
312 - def set_value(self, value):
313 """Set value to C{value}. 314 315 :param value: The value to assign. 316 :type value: bool 317 """ 318 self._value = 1 if value else 0
319
320 -class Char(BasicBase, EditableLineEdit):
321 """Implementation of an (unencoded) 8-bit character.""" 322
323 - def __init__(self, **kwargs):
324 """Initialize the character.""" 325 super(Char, self).__init__(**kwargs) 326 self._value = _b00
327
328 - def get_value(self):
329 """Return stored value. 330 331 :return: The stored value. 332 """ 333 return self._value
334
335 - def set_value(self, value):
336 """Set character to C{value}. 337 338 :param value: The value to assign (bytes of length 1). 339 :type value: bytes 340 """ 341 assert(isinstance(value, _bytes)) 342 assert(len(value) == 1) 343 self._value = value
344
345 - def read(self, stream, data):
346 """Read value from stream. 347 348 :param stream: The stream to read from. 349 :type stream: file 350 """ 351 self._value = stream.read(1)
352
353 - def write(self, stream, data):
354 """Write value to stream. 355 356 :param stream: The stream to write to. 357 :type stream: file 358 """ 359 stream.write(self._value)
360
361 - def __str__(self):
362 return _as_str(self._value)
363
364 - def get_size(self, data=None):
365 """Return number of bytes this type occupies in a file. 366 367 :return: Number of bytes. 368 """ 369 return 1
370
371 - def get_hash(self, data=None):
372 """Return a hash value for this value. 373 374 :return: An immutable object that can be used as a hash. 375 """ 376 self.get_value()
377
378 -class Float(BasicBase, EditableFloatSpinBox):
379 """Implementation of a 32-bit float.""" 380
381 - def __init__(self, **kwargs):
382 """Initialize the float.""" 383 super(Float, self).__init__(**kwargs) 384 self._value = 0
385
386 - def get_value(self):
387 """Return stored value. 388 389 :return: The stored value. 390 """ 391 return self._value
392
393 - def set_value(self, value):
394 """Set value to C{value}. 395 396 :param value: The value to assign. 397 :type value: float 398 """ 399 self._value = float(value)
400
401 - def read(self, stream, data):
402 """Read value from stream. 403 404 :param stream: The stream to read from. 405 :type stream: file 406 """ 407 self._value = struct.unpack(data._byte_order + 'f', 408 stream.read(4))[0]
409
410 - def write(self, stream, data):
411 """Write value to stream. 412 413 :param stream: The stream to write to. 414 :type stream: file 415 """ 416 try: 417 stream.write(struct.pack(data._byte_order + 'f', 418 self._value)) 419 except OverflowError: 420 logger = logging.getLogger("pyffi.object_models") 421 logger.warn("float value overflow, writing NaN") 422 stream.write(struct.pack(data._byte_order + 'I', 423 0x7fc00000))
424
425 - def get_size(self, data=None):
426 """Return number of bytes this type occupies in a file. 427 428 :return: Number of bytes. 429 """ 430 return 4
431
432 - def get_hash(self, data=None):
433 """Return a hash value for this value. Currently implemented 434 with precision 1/200. 435 436 :return: An immutable object that can be used as a hash. 437 """ 438 return int(self.get_value()*200)
439
440 -class ZString(BasicBase, EditableLineEdit):
441 """String of variable length (null terminated). 442 443 >>> from tempfile import TemporaryFile 444 >>> f = TemporaryFile() 445 >>> s = ZString() 446 >>> if f.write('abcdefghijklmnopqrst\\x00'.encode("ascii")): pass # b'abc...' 447 >>> if f.seek(0): pass # ignore result for py3k 448 >>> s.read(f) 449 >>> str(s) 450 'abcdefghijklmnopqrst' 451 >>> if f.seek(0): pass # ignore result for py3k 452 >>> s.set_value('Hi There!') 453 >>> s.write(f) 454 >>> if f.seek(0): pass # ignore result for py3k 455 >>> m = ZString() 456 >>> m.read(f) 457 >>> str(m) 458 'Hi There!' 459 """ 460 _maxlen = 1000 #: The maximum length. 461
462 - def __init__(self, **kwargs):
463 """Initialize the string.""" 464 super(ZString, self).__init__(**kwargs) 465 self._value = _b
466
467 - def __str__(self):
468 return _as_str(self._value)
469
470 - def get_value(self):
471 """Return the string. 472 473 :return: The stored string. 474 :rtype: C{bytes} 475 """ 476 return _as_str(self._value)
477
478 - def set_value(self, value):
479 """Set string to C{value}. 480 481 :param value: The value to assign. 482 :type value: ``str`` (will be encoded as default) or C{bytes} 483 """ 484 val = _as_bytes(value) 485 i = val.find(_b00) 486 if i != -1: 487 val = val[:i] 488 if len(val) > self._maxlen: 489 raise ValueError('string too long') 490 self._value = val
491
492 - def read(self, stream, data=None):
493 """Read string from stream. 494 495 :param stream: The stream to read from. 496 :type stream: file 497 """ 498 i = 0 499 val = _b 500 char = _b 501 while char != _b00: 502 i += 1 503 if i > self._maxlen: 504 raise ValueError('string too long') 505 val += char 506 char = stream.read(1) 507 self._value = val
508
509 - def write(self, stream, data=None):
510 """Write string to stream. 511 512 :param stream: The stream to write to. 513 :type stream: file 514 """ 515 stream.write(self._value) 516 stream.write(_b00)
517
518 - def get_size(self, data=None):
519 """Return number of bytes this type occupies in a file. 520 521 :return: Number of bytes. 522 """ 523 return len(self._value) + 1
524
525 - def get_hash(self, data=None):
526 """Return a hash value for this string. 527 528 :return: An immutable object that can be used as a hash. 529 """ 530 return self._value
531
532 -class FixedString(BasicBase, EditableLineEdit):
533 """String of fixed length. Default length is 0, so you must override 534 this class and set the _len class variable. 535 536 >>> from tempfile import TemporaryFile 537 >>> f = TemporaryFile() 538 >>> class String8(FixedString): 539 ... _len = 8 540 >>> s = String8() 541 >>> if f.write('abcdefghij'.encode()): pass # ignore result for py3k 542 >>> if f.seek(0): pass # ignore result for py3k 543 >>> s.read(f) 544 >>> str(s) 545 'abcdefgh' 546 >>> if f.seek(0): pass # ignore result for py3k 547 >>> s.set_value('Hi There') 548 >>> s.write(f) 549 >>> if f.seek(0): pass # ignore result for py3k 550 >>> m = String8() 551 >>> m.read(f) 552 >>> str(m) 553 'Hi There' 554 """ 555 _len = 0 556
557 - def __init__(self, **kwargs):
558 """Initialize the string.""" 559 super(FixedString, self).__init__(**kwargs) 560 self._value = _b
561
562 - def __str__(self):
563 return _as_str(self._value)
564
565 - def get_value(self):
566 """Return the string. 567 568 :return: The stored string. 569 :rtype: C{bytes} 570 """ 571 return self._value
572
573 - def set_value(self, value):
574 """Set string to C{value}. 575 576 :param value: The value to assign. 577 :type value: ``str`` (encoded as default) or C{bytes} 578 """ 579 val = _as_bytes(value) 580 if len(val) > self._len: 581 raise ValueError("string '%s' too long" % val) 582 self._value = val
583
584 - def read(self, stream, data=None):
585 """Read string from stream. 586 587 :param stream: The stream to read from. 588 :type stream: file 589 """ 590 self._value = stream.read(self._len) 591 i = self._value.find(_b00) 592 if i != -1: 593 self._value = self._value[:i]
594
595 - def write(self, stream, data=None):
596 """Write string to stream. 597 598 :param stream: The stream to write to. 599 :type stream: file 600 """ 601 stream.write(self._value.ljust(self._len, _b00))
602
603 - def get_size(self, data=None):
604 """Return number of bytes this type occupies in a file. 605 606 :return: Number of bytes. 607 """ 608 return self._len
609
610 - def get_hash(self, data=None):
611 """Return a hash value for this string. 612 613 :return: An immutable object that can be used as a hash. 614 """ 615 return self._value
616
617 -class SizedString(BasicBase, EditableLineEdit):
618 """Basic type for strings. The type starts with an unsigned int which 619 describes the length of the string. 620 621 >>> from tempfile import TemporaryFile 622 >>> f = TemporaryFile() 623 >>> from pyffi.object_models import FileFormat 624 >>> data = FileFormat.Data() 625 >>> s = SizedString() 626 >>> if f.write('\\x07\\x00\\x00\\x00abcdefg'.encode("ascii")): pass # ignore result for py3k 627 >>> if f.seek(0): pass # ignore result for py3k 628 >>> s.read(f, data) 629 >>> str(s) 630 'abcdefg' 631 >>> if f.seek(0): pass # ignore result for py3k 632 >>> s.set_value('Hi There') 633 >>> s.write(f, data) 634 >>> if f.seek(0): pass # ignore result for py3k 635 >>> m = SizedString() 636 >>> m.read(f, data) 637 >>> str(m) 638 'Hi There' 639 """ 640
641 - def __init__(self, **kwargs):
642 """Initialize the string.""" 643 super(SizedString, self).__init__(**kwargs) 644 self._value = _b
645
646 - def get_value(self):
647 """Return the string. 648 649 :return: The stored string. 650 """ 651 return self._value
652
653 - def set_value(self, value):
654 """Set string to C{value}. 655 656 :param value: The value to assign. 657 :type value: str 658 """ 659 val = _as_bytes(value) 660 if len(val) > 10000: 661 raise ValueError('string too long') 662 self._value = val
663
664 - def __str__(self):
665 return _as_str(self._value)
666
667 - def get_size(self, data=None):
668 """Return number of bytes this type occupies in a file. 669 670 :return: Number of bytes. 671 """ 672 return 4 + len(self._value)
673
674 - def get_hash(self, data=None):
675 """Return a hash value for this string. 676 677 :return: An immutable object that can be used as a hash. 678 """ 679 return self.get_value()
680
681 - def read(self, stream, data):
682 """Read string from stream. 683 684 :param stream: The stream to read from. 685 :type stream: file 686 """ 687 length, = struct.unpack(data._byte_order + 'I', 688 stream.read(4)) 689 if length > 10000: 690 raise ValueError('string too long (0x%08X at 0x%08X)' 691 % (length, stream.tell())) 692 self._value = stream.read(length)
693
694 - def write(self, stream, data):
695 """Write string to stream. 696 697 :param stream: The stream to write to. 698 :type stream: file 699 """ 700 stream.write(struct.pack(data._byte_order + 'I', 701 len(self._value))) 702 stream.write(self._value)
703
704 -class UndecodedData(BasicBase):
705 """Basic type for undecoded data trailing at the end of a file."""
706 - def __init__(self, **kwargs):
707 BasicBase.__init__(self, **kwargs) 708 self._value = _b
709
710 - def get_value(self):
711 """Return stored value. 712 713 :return: The stored value. 714 """ 715 return self._value
716
717 - def set_value(self, value):
718 """Set value to C{value}. 719 720 :param value: The value to assign. 721 :type value: bytes 722 """ 723 if len(value) > 16000000: 724 raise ValueError('data too long') 725 self._value = value
726
727 - def __str__(self):
728 return '<UNDECODED DATA>'
729
730 - def get_size(self, data=None):
731 """Return number of bytes the data occupies in a file. 732 733 :return: Number of bytes. 734 """ 735 return len(self._value)
736
737 - def get_hash(self, data=None):
738 """Return a hash value for this value. 739 740 :return: An immutable object that can be used as a hash. 741 """ 742 return self.get_value()
743
744 - def read(self, stream, data):
745 """Read data from stream. Note that this function simply 746 reads until the end of the stream. 747 748 :param stream: The stream to read from. 749 :type stream: file 750 """ 751 self._value = stream.read(-1)
752
753 - def write(self, stream, data):
754 """Write data to stream. 755 756 :param stream: The stream to write to. 757 :type stream: file 758 """ 759 stream.write(self._value)
760