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

Source Code for Module pyffi.object_models.binary_type

  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   
 42  from pyffi.object_models.any_type import AnyType 
 43  from pyffi.object_models.simple_type import SimpleType 
 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  # Base classes 
 50   
51 -class BinaryType(AnyType):
52 """Abstract base class for binary data types."""
53 - def get_size(self):
54 raise NotImplementedError
55
56 -class BinarySimpleType(SimpleType, BinaryType):
57 """Abstract base class for binary data types.""" 58 pass
59 60 # Helper objects and helper functions (private) 61 62 _b = "".encode("ascii") # py3k's b"" 63 _b00 = "\x00".encode("ascii") # py3k's b"\x00" 64 65 66 # supports the bytes object for < py26 67 try: 68 bytes 69 except NameError: 70 bytes = str # for py25 backwards compatibility 71 72 if bytes is str: 73 # < py3k: str for byte strings, unicode for text strings 74 _bytes = str 75 _str = unicode 76 else: 77 # >= py3k: bytes for byte strings, str for text strings 78 _bytes = bytes 79 _str = str 80
81 -def _as_bytes(value):
82 """Helper function which converts a string to _bytes (this is useful for 83 set_value in all string classes, which use bytes for representation). 84 85 :return: The bytes representing the value. 86 :rtype: C{_bytes} 87 88 >>> # following doctest fails on py3k, hence disabled 89 >>> _as_bytes(u"\\u00e9defa") == u"\\u00e9defa".encode("utf-8") # doctest: +SKIP 90 True 91 92 >>> _as_bytes(123) # doctest: +ELLIPSIS 93 Traceback (most recent call last): 94 ... 95 TypeError: ... 96 """ 97 if isinstance(value, _bytes): 98 return value 99 elif isinstance(value, _str): 100 return value.encode("utf-8", "replace") 101 else: 102 raise TypeError("expected %s or %s" % (_bytes.__name__, _str.__name__))
103
104 -def _as_str(value):
105 """Helper function to convert bytes back to str. This is used in 106 the __str__ functions for simple string types. If you want a custom 107 encoding, use an explicit decode call on the value. 108 """ 109 if not isinstance(value, (_str, _bytes)): 110 raise TypeError("expected %s or %s" % (_bytes.__name__, _str.__name__)) 111 elif not value: 112 return '' 113 elif isinstance(value, str): 114 # this always works regardless of the python version 115 return value 116 elif isinstance(value, _bytes): 117 # >= py3k: simply decode 118 return value.decode("utf-8", "replace") 119 elif isinstance(value, unicode): 120 # < py3k: use ascii encoding to produce a str 121 # (this avoids unicode errors) 122 return value.encode("ascii", "replace")
123 124 # SimpleType implementations for common binary types 125
126 -class IntType(BinarySimpleType, EditableSpinBox):
127 """Basic implementation of a 32-bit signed integer type. Also serves as a 128 base class for all other integer types. 129 130 >>> from tempfile import TemporaryFile 131 >>> tmp = TemporaryFile() 132 >>> i = IntType() 133 >>> i.value = -1 134 >>> i.value 135 -1 136 >>> i.value = 0x11223344 137 >>> i.write(tmp) 138 >>> j = IntType() 139 >>> if tmp.seek(0): pass # ignore result for py3k 140 >>> j.read(tmp) 141 >>> hex(j.value) 142 '0x11223344' 143 >>> i.value = 2**40 # doctest: +ELLIPSIS 144 Traceback (most recent call last): 145 ... 146 ValueError: ... 147 >>> i.value = 'hello world' 148 Traceback (most recent call last): 149 ... 150 ValueError: cannot convert value 'hello world' to integer 151 >>> if tmp.seek(0): pass # ignore result for py3k 152 >>> if tmp.write('\x11\x22\x33\x44'.encode("ascii")): pass # b'\x11\x22\x33\x44' 153 >>> if tmp.seek(0): pass # ignore result for py3k 154 >>> i.read(tmp) 155 >>> hex(i.value) 156 '0x44332211' 157 """ 158 159 _min = -0x80000000 #: Minimum value. 160 _max = 0x7fffffff #: Maximum value. 161 _struct = 'i' #: Character used to represent type in struct. 162 _size = 4 #: Number of bytes. 163 164 # SimpleType 165
166 - def __init__(self):
167 """Initialize the integer.""" 168 self._value = 0
169
170 - def set_value(self, value):
171 """Set value to C{value}. Calls C{int(value)} to convert to integer. 172 173 :param value: The value to assign. 174 :type value: int 175 """ 176 try: 177 val = int(value) 178 except ValueError: 179 try: 180 val = int(value, 16) # for '0x...' strings 181 except ValueError: 182 try: 183 val = getattr(self, value) # for enums 184 except AttributeError: 185 raise ValueError( 186 "cannot convert value '%s' to integer"%value) 187 if val < self._min or val > self._max: 188 raise ValueError('value out of range (%i)' % val) 189 self._value = val
190 191 # AnyType 192
193 - def read(self, stream):
194 """Read value from stream. 195 196 :param stream: The stream to read from. 197 :type stream: ``file`` 198 """ 199 self._value = struct.unpack('<' + self._struct, 200 stream.read(self._size))[0]
201
202 - def write(self, stream):
203 """Write value to stream. 204 205 :param stream: The stream to write to. 206 :type stream: ``file`` 207 """ 208 stream.write(struct.pack('<' + self._struct, self._value))
209 210 # BinaryType 211
212 - def get_size(self):
213 """Return number of bytes this type occupies in a file. 214 215 :return: Number of bytes. 216 """ 217 return self._size
218 219 # EditableSpinBox 220
221 - def get_editor_minimum(self):
222 """Minimum possible value. 223 224 :return: Minimum possible value. 225 """ 226 return self._min
227
228 - def get_editor_maximum(self):
229 """Maximum possible value. 230 231 :return: Maximum possible value. 232 """ 233 return self._max
234
235 -class UIntType(IntType):
236 """Implementation of a 32-bit unsigned integer type.""" 237 _min = 0 238 _max = 0xffffffff 239 _struct = 'I' 240 _size = 4
241
242 -class ByteType(IntType):
243 """Implementation of a 8-bit signed integer type.""" 244 _min = -0x80 245 _max = 0x7f 246 _struct = 'b' 247 _size = 1
248
249 -class UByteType(IntType):
250 """Implementation of a 8-bit unsigned integer type.""" 251 _min = 0 252 _max = 0xff 253 _struct = 'B' 254 _size = 1
255
256 -class ShortType(IntType):
257 """Implementation of a 16-bit signed integer type.""" 258 _min = -0x8000 259 _max = 0x7fff 260 _struct = 'h' 261 _size = 2
262
263 -class UShortType(UIntType):
264 """Implementation of a 16-bit unsigned integer type.""" 265 _min = 0 266 _max = 0xffff 267 _struct = 'H' 268 _size = 2
269
270 -class BoolType(UByteType, EditableBoolComboBox):
271 """Simple bool implementation.""" 272
273 - def get_value(self):
274 """Return stored value. 275 276 :return: The stored value. 277 """ 278 return bool(self._value)
279
280 - def set_value(self, value):
281 """Set value to C{value}. 282 283 :param value: The value to assign. 284 :type value: bool 285 """ 286 if not isinstance(value, bool): 287 raise TypeError("expected a bool") 288 self._value = 1 if value else 0
289
290 -class CharType(BinarySimpleType, EditableLineEdit):
291 """Implementation of an (unencoded) 8-bit character.""" 292
293 - def __init__(self):
294 """Initialize the character.""" 295 self._value = _b00
296
297 - def set_value(self, value):
298 """Set character to C{value}. 299 300 :param value: The value to assign (bytes of length 1). 301 :type value: bytes 302 """ 303 assert(isinstance(value, _bytes)) 304 assert(len(value) == 1) 305 self._value = value
306
307 - def read(self, stream):
308 """Read value from stream. 309 310 :param stream: The stream to read from. 311 :type stream: file 312 """ 313 self._value = stream.read(1)
314
315 - def write(self, stream):
316 """Write value to stream. 317 318 :param stream: The stream to write to. 319 :type stream: file 320 """ 321 stream.write(self._value)
322
323 - def __str__(self):
324 return _as_str(self._value)
325
326 - def get_size(self):
327 """Return number of bytes this type occupies in a file. 328 329 :return: Number of bytes. 330 """ 331 return 1
332
333 -class Float(BinarySimpleType, EditableFloatSpinBox):
334 """Implementation of a 32-bit float.""" 335
336 - def __init__(self):
337 """Initialize the float.""" 338 self._value = 0.0
339
340 - def set_value(self, value):
341 """Set value to C{value}. 342 343 :param value: The value to assign. 344 :type value: float 345 """ 346 self._value = float(value)
347
348 - def read(self, stream):
349 """Read value from stream. 350 351 :param stream: The stream to read from. 352 :type stream: file 353 """ 354 self._value = struct.unpack('<f', stream.read(4))[0]
355
356 - def write(self, stream):
357 """Write value to stream. 358 359 :param stream: The stream to write to. 360 :type stream: file 361 """ 362 stream.write(struct.pack('<f', self._value))
363
364 - def get_size(self):
365 """Return number of bytes this type occupies in a file. 366 367 :return: Number of bytes. 368 """ 369 return 4
370
371 -class ZString(BinarySimpleType, EditableLineEdit):
372 """String of variable length (null terminated). 373 374 >>> from tempfile import TemporaryFile 375 >>> f = TemporaryFile() 376 >>> s = ZString() 377 >>> if f.write('abcdefghijklmnopqrst\\x00'.encode("ascii")): pass # b'abc...' 378 >>> if f.seek(0): pass # ignore result for py3k 379 >>> s.read(f) 380 >>> str(s) 381 'abcdefghijklmnopqrst' 382 >>> if f.seek(0): pass # ignore result for py3k 383 >>> s.value = 'Hi There!' 384 >>> s.write(f) 385 >>> if f.seek(0): pass # ignore result for py3k 386 >>> m = ZString() 387 >>> m.read(f) 388 >>> str(m) 389 'Hi There!' 390 """ 391 _maxlen = 1000 #: The maximum length. 392
393 - def __init__(self):
394 """Initialize the string.""" 395 self._value = _b
396
397 - def __str__(self):
398 return _as_str(self._value)
399
400 - def set_value(self, value):
401 """Set string to C{value}. 402 403 :param value: The value to assign. 404 :type value: ``str`` (will be encoded as default) or C{bytes} 405 """ 406 val = _as_bytes(value) 407 i = val.find(_b00) 408 if i != -1: 409 val = val[:i] 410 if len(val) > self._maxlen: 411 raise ValueError('string too long') 412 self._value = val
413
414 - def read(self, stream):
415 """Read string from stream. 416 417 :param stream: The stream to read from. 418 :type stream: file 419 """ 420 i = 0 421 val = _b 422 char = _b 423 while char != _b00: 424 i += 1 425 if i > self._maxlen: 426 raise ValueError('string too long') 427 val += char 428 char = stream.read(1) 429 self._value = val
430
431 - def write(self, stream):
432 """Write string to stream. 433 434 :param stream: The stream to write to. 435 :type stream: file 436 """ 437 stream.write(self._value) 438 stream.write(_b00)
439
440 - def get_size(self):
441 """Return number of bytes this type occupies in a file. 442 443 :return: Number of bytes. 444 """ 445 return len(self._value) + 1
446
447 -class FixedString(BinarySimpleType, EditableLineEdit):
448 """String of fixed length. Default length is 0, so you must override 449 this class and set the _len class variable. 450 451 >>> from tempfile import TemporaryFile 452 >>> f = TemporaryFile() 453 >>> class String8(FixedString): 454 ... _len = 8 455 >>> s = String8() 456 >>> if f.write('abcdefghij'.encode()): pass # ignore result for py3k 457 >>> if f.seek(0): pass # ignore result for py3k 458 >>> s.read(f) 459 >>> str(s) 460 'abcdefgh' 461 >>> if f.seek(0): pass # ignore result for py3k 462 >>> s.value = 'Hi There' 463 >>> s.write(f) 464 >>> if f.seek(0): pass # ignore result for py3k 465 >>> m = String8() 466 >>> m.read(f) 467 >>> str(m) 468 'Hi There' 469 """ 470 _len = 0 471
472 - def __init__(self):
473 """Initialize the string.""" 474 self._value = _b
475
476 - def __str__(self):
477 return _as_str(self._value)
478
479 - def get_value(self):
480 """Return the string. 481 482 :return: The stored string. 483 :rtype: C{bytes} 484 """ 485 return self._value
486
487 - def set_value(self, value):
488 """Set string to C{value}. 489 490 :param value: The value to assign. 491 :type value: ``str`` (encoded as default) or C{bytes} 492 """ 493 val = _as_bytes(value) 494 if len(val) > self._len: 495 raise ValueError("string '%s' too long" % val) 496 self._value = val
497
498 - def read(self, stream):
499 """Read string from stream. 500 501 :param stream: The stream to read from. 502 :type stream: file 503 """ 504 self._value = stream.read(self._len) 505 i = self._value.find(_b00) 506 if i != -1: 507 self._value = self._value[:i]
508
509 - def write(self, stream):
510 """Write string to stream. 511 512 :param stream: The stream to write to. 513 :type stream: file 514 """ 515 stream.write(self._value.ljust(self._len, _b00))
516
517 - def get_size(self):
518 """Return number of bytes this type occupies in a file. 519 520 :return: Number of bytes. 521 """ 522 return self._len
523
524 -class SizedString(BinarySimpleType, EditableLineEdit):
525 """Basic type for strings. The type starts with an unsigned int which 526 describes the length of the string. 527 528 >>> from tempfile import TemporaryFile 529 >>> f = TemporaryFile() 530 >>> s = SizedString() 531 >>> if f.write('\\x07\\x00\\x00\\x00abcdefg'.encode("ascii")): pass # ignore result for py3k 532 >>> if f.seek(0): pass # ignore result for py3k 533 >>> s.read(f) 534 >>> str(s) 535 'abcdefg' 536 >>> if f.seek(0): pass # ignore result for py3k 537 >>> s.set_value('Hi There') 538 >>> s.write(f) 539 >>> if f.seek(0): pass # ignore result for py3k 540 >>> m = SizedString() 541 >>> m.read(f) 542 >>> str(m) 543 'Hi There' 544 """ 545
546 - def __init__(self):
547 """Initialize the string.""" 548 self._value = _b
549
550 - def get_value(self):
551 """Return the string. 552 553 :return: The stored string. 554 """ 555 return self._value
556
557 - def set_value(self, value):
558 """Set string to C{value}. 559 560 :param value: The value to assign. 561 :type value: str 562 """ 563 val = _as_bytes(value) 564 if len(val) > 10000: 565 raise ValueError('string too long') 566 self._value = val
567
568 - def __str__(self):
569 return _as_str(self._value)
570
571 - def get_size(self):
572 """Return number of bytes this type occupies in a file. 573 574 :return: Number of bytes. 575 """ 576 return 4 + len(self._value)
577
578 - def read(self, stream):
579 """Read string from stream. 580 581 :param stream: The stream to read from. 582 :type stream: file 583 """ 584 length, = struct.unpack('<I', stream.read(4)) 585 if length > 10000: 586 raise ValueError('string too long (0x%08X at 0x%08X)' 587 % (length, stream.tell())) 588 self._value = stream.read(length)
589
590 - def write(self, stream):
591 """Write string to stream. 592 593 :param stream: The stream to write to. 594 :type stream: file 595 """ 596 stream.write(struct.pack('<I', len(self._value))) 597 stream.write(self._value)
598
599 -class UndecodedData(SimpleType, BinaryType):
600 """Basic type for undecoded data trailing at the end of a file."""
601 - def __init__(self):
602 self._value = _b
603
604 - def get_value(self):
605 """Return stored value. 606 607 :return: The stored value. 608 """ 609 return self._value
610
611 - def set_value(self, value):
612 """Set value to C{value}. 613 614 :param value: The value to assign. 615 :type value: bytes 616 """ 617 if len(value) > 16000000: 618 raise ValueError('data too long') 619 self._value = value
620
621 - def __str__(self):
622 return '<UNDECODED DATA>'
623
624 - def get_size(self):
625 """Return number of bytes the data occupies in a file. 626 627 :return: Number of bytes. 628 """ 629 return len(self._value)
630
631 - def read(self, stream):
632 """Read data from stream. Note that this function simply 633 reads until the end of the stream. 634 635 :param stream: The stream to read from. 636 :type stream: file 637 """ 638 self._value = stream.read(-1)
639
640 - def write(self, stream):
641 """Write data to stream. 642 643 :param stream: The stream to write to. 644 :type stream: file 645 """ 646 stream.write(self._value)
647