1 """Implements common basic types in XML file format descriptions."""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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")
50 _b00 = "\x00".encode("ascii")
51
52
53 try:
54 bytes
55 except NameError:
56 bytes = str
57
58 if bytes is str:
59
60 _bytes = str
61 _str = unicode
62 else:
63
64 _bytes = bytes
65 _str = str
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
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
101 return value
102 elif isinstance(value, _bytes):
103
104 return value.decode("utf-8", "replace")
105 elif isinstance(value, unicode):
106
107
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
146 _max = 0x7fffffff
147 _struct = 'i'
148 _size = 4
149
151 """Initialize the integer."""
152 super(Int, self).__init__(**kwargs)
153 self._value = 0
154
156 """Return stored value.
157
158 :return: The stored value.
159 """
160 return self._value
161
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)
173 except ValueError:
174 try:
175 val = getattr(self, value)
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):
191
192 - def write(self, stream, data):
199
202
203 @classmethod
205 """Return number of bytes this type occupies in a file.
206
207 :return: Number of bytes.
208 """
209 return cls._size
210
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
219 """Minimum possible value.
220
221 :return: Minimum possible value.
222 """
223 return self._min
224
226 """Maximum possible value.
227
228 :return: Maximum possible value.
229 """
230 return self._max
231
233 """Implementation of a 32-bit unsigned integer type."""
234 _min = 0
235 _max = 0xffffffff
236 _struct = 'I'
237 _size = 4
238
240 """Implementation of a 64-bit signed integer type."""
241 _min = -0x8000000000000000
242 _max = 0x7fffffffffffffff
243 _struct = 'q'
244 _size = 8
245
247 """Implementation of a 64-bit unsigned integer type."""
248 _min = 0
249 _max = 0xffffffffffffffff
250 _struct = 'Q'
251 _size = 8
252
254 """Implementation of a 8-bit signed integer type."""
255 _min = -0x80
256 _max = 0x7f
257 _struct = 'b'
258 _size = 1
259
261 """Implementation of a 8-bit unsigned integer type."""
262 _min = 0
263 _max = 0xff
264 _struct = 'B'
265 _size = 1
266
268 """Implementation of a 16-bit signed integer type."""
269 _min = -0x8000
270 _max = 0x7fff
271 _struct = 'h'
272 _size = 2
273
275 """Implementation of a 16-bit unsigned integer type."""
276 _min = 0
277 _max = 0xffff
278 _struct = 'H'
279 _size = 2
280
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
306 """Return stored value.
307
308 :return: The stored value.
309 """
310 return bool(self._value)
311
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
327
329 """Return stored value.
330
331 :return: The stored value.
332 """
333 return self._value
334
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
363
365 """Return number of bytes this type occupies in a file.
366
367 :return: Number of bytes.
368 """
369 return 1
370
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
385
387 """Return stored value.
388
389 :return: The stored value.
390 """
391 return self._value
392
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):
424
426 """Return number of bytes this type occupies in a file.
427
428 :return: Number of bytes.
429 """
430 return 4
431
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
461
466
469
471 """Return the string.
472
473 :return: The stored string.
474 :rtype: C{bytes}
475 """
476 return _as_str(self._value)
477
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
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
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
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
561
564
566 """Return the string.
567
568 :return: The stored string.
569 :rtype: C{bytes}
570 """
571 return self._value
572
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
604 """Return number of bytes this type occupies in a file.
605
606 :return: Number of bytes.
607 """
608 return self._len
609
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
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
645
647 """Return the string.
648
649 :return: The stored string.
650 """
651 return self._value
652
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
666
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
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):
693
694 - def write(self, stream, data):
703
705 """Basic type for undecoded data trailing at the end of a file."""
709
711 """Return stored value.
712
713 :return: The stored value.
714 """
715 return self._value
716
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
728 return '<UNDECODED DATA>'
729
731 """Return number of bytes the data occupies in a file.
732
733 :return: Number of bytes.
734 """
735 return len(self._value)
736
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