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
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
50
52 """Abstract base class for binary data types."""
54 raise NotImplementedError
55
57 """Abstract base class for binary data types."""
58 pass
59
60
61
62 _b = "".encode("ascii")
63 _b00 = "\x00".encode("ascii")
64
65
66
67 try:
68 bytes
69 except NameError:
70 bytes = str
71
72 if bytes is str:
73
74 _bytes = str
75 _str = unicode
76 else:
77
78 _bytes = bytes
79 _str = str
80
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
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
115 return value
116 elif isinstance(value, _bytes):
117
118 return value.decode("utf-8", "replace")
119 elif isinstance(value, unicode):
120
121
122 return value.encode("ascii", "replace")
123
124
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
160 _max = 0x7fffffff
161 _struct = 'i'
162 _size = 4
163
164
165
167 """Initialize the integer."""
168 self._value = 0
169
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)
181 except ValueError:
182 try:
183 val = getattr(self, value)
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
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
211
213 """Return number of bytes this type occupies in a file.
214
215 :return: Number of bytes.
216 """
217 return self._size
218
219
220
222 """Minimum possible value.
223
224 :return: Minimum possible value.
225 """
226 return self._min
227
229 """Maximum possible value.
230
231 :return: Maximum possible value.
232 """
233 return self._max
234
236 """Implementation of a 32-bit unsigned integer type."""
237 _min = 0
238 _max = 0xffffffff
239 _struct = 'I'
240 _size = 4
241
243 """Implementation of a 8-bit signed integer type."""
244 _min = -0x80
245 _max = 0x7f
246 _struct = 'b'
247 _size = 1
248
250 """Implementation of a 8-bit unsigned integer type."""
251 _min = 0
252 _max = 0xff
253 _struct = 'B'
254 _size = 1
255
257 """Implementation of a 16-bit signed integer type."""
258 _min = -0x8000
259 _max = 0x7fff
260 _struct = 'h'
261 _size = 2
262
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
274 """Return stored value.
275
276 :return: The stored value.
277 """
278 return bool(self._value)
279
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
294 """Initialize the character."""
295 self._value = _b00
296
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
325
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
337 """Initialize the float."""
338 self._value = 0.0
339
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
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
392
394 """Initialize the string."""
395 self._value = _b
396
399
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
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
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
473 """Initialize the string."""
474 self._value = _b
475
478
480 """Return the string.
481
482 :return: The stored string.
483 :rtype: C{bytes}
484 """
485 return self._value
486
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
518 """Return number of bytes this type occupies in a file.
519
520 :return: Number of bytes.
521 """
522 return self._len
523
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
547 """Initialize the string."""
548 self._value = _b
549
551 """Return the string.
552
553 :return: The stored string.
554 """
555 return self._value
556
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
570
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
600 """Basic type for undecoded data trailing at the end of a file."""
603
605 """Return stored value.
606
607 :return: The stored value.
608 """
609 return self._value
610
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
622 return '<UNDECODED DATA>'
623
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