1 """Implements base class for bitstruct types."""
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
41
42
43
44 from functools import partial
45 from itertools import izip
46 import struct
47
48 from pyffi.object_models.editable import EditableSpinBox
49 from pyffi.utils.graph import DetailNode, EdgeFilter
93
94 -class Bits(DetailNode, EditableSpinBox):
95 """Basic implementation of a n-bit unsigned integer type (without read
96 and write)."""
97 - def __init__(self, numbits = 1, default = 0, parent = None):
102
104 """Return stored value."""
105 return self._value
106
108 """Set value to C{value}."""
109 if not isinstance(value, (int, long)):
110 raise TypeError("bitstruct attribute must be integer")
111 if value >> self._numbits:
112 raise ValueError('value out of range (%i)' % value)
113 self._value = value
114
117
118
119
121 """Return an object that can be used to display the instance."""
122 return str(self._value)
123
124
125
128
131
134
136 return (1 << self._numbits) - 1
137
139 """Base class from which all file bitstruct types are derived.
140
141 The BitStructBase class implements the basic bitstruct interface:
142 it will initialize all attributes using the class interface
143 using the _attrs class variable, represent them as strings, and so on.
144 The class variable _attrs must be declared every derived class
145 interface.
146
147 Each item in the class _attrs list stores the information about
148 the attribute as stored for instance in the xml file, and the
149 _<name>_value_ instance variable stores the actual attribute
150 instance.
151
152 Direct access to the attributes is implemented using a <name>
153 property which invokes the get_attribute and set_attribute
154 functions, as demonstrated below.
155
156 See the pyffi.XmlHandler class for a more advanced example.
157
158 >>> from pyffi.object_models.xml.basic import BasicBase
159 >>> from pyffi.object_models.xml.expression import Expression
160 >>> from pyffi.object_models.xml import BitStructAttribute as Attr
161 >>> class SimpleFormat(object):
162 ... @staticmethod
163 ... def name_attribute(name):
164 ... return name
165 >>> class Flags(BitStructBase):
166 ... _numbytes = 1
167 ... _attrs = [
168 ... Attr(SimpleFormat, dict(name = 'a', numbits = '3')),
169 ... Attr(SimpleFormat, dict(name = 'b', numbits = '1'))]
170 >>> SimpleFormat.Flags = Flags
171 >>> y = Flags()
172 >>> y.a = 5
173 >>> y.b = 1
174 >>> print(y) # doctest:+ELLIPSIS
175 <class 'pyffi.object_models.xml.bit_struct.Flags'> instance at 0x...
176 * a : 5
177 * b : 1
178 <BLANKLINE>
179 >>> y.to_int(None)
180 13
181 >>> y.from_int(9, None)
182 >>> print(y) # doctest:+ELLIPSIS
183 <class 'pyffi.object_models.xml.bit_struct.Flags'> instance at 0x...
184 * a : 1
185 * b : 1
186 <BLANKLINE>
187 """
188
189 __metaclass__ = _MetaBitStructBase
190
191 _attrs = []
192 _numbytes = 1
193 _games = {}
194 arg = None
195
196
197 - def __init__(self, template = None, argument = None, parent = None):
198 """The constructor takes a tempate: any attribute whose type,
199 or template type, is type(None) - which corresponds to
200 TEMPLATE in the xml description - will be replaced by this
201 type. The argument is what the ARG xml tags will be replaced with.
202
203 :param template: If the class takes a template type
204 argument, then this argument describes the template type.
205 :param argument: If the class takes a type argument, then
206 it is described here.
207 :param parent: The parent of this instance, that is, the instance this
208 array is an attribute of."""
209
210
211 names = []
212
213 self.arg = argument
214
215
216
217
218
219 self._items = []
220
221 for attr in self._attribute_list:
222
223 if attr.name in names:
224 continue
225 names.append(attr.name)
226
227
228 if attr.default != None:
229 attr_instance = Bits(numbits = attr.numbits,
230 default = attr.default,
231 parent = self)
232 else:
233 attr_instance = Bits(numbits = attr.numbits,
234 parent = self)
235
236
237 setattr(self, "_%s_value_" % attr.name, attr_instance)
238
239
240 self._items.append(attr_instance)
241
243 """Copy attributes from a given block (one block class must be a
244 subclass of the other). Returns self."""
245
246 if isinstance(self, block.__class__):
247 attrlist = block._get_filtered_attribute_list()
248 elif isinstance(block, self.__class__):
249 attrlist = self._get_filtered_attribute_list()
250 else:
251 raise ValueError("deepcopy: classes %s and %s unrelated"
252 % (self.__class__.__name__, block.__class__.__name__))
253
254 for attr in attrlist:
255 setattr(self, attr.name, getattr(block, attr.name))
256
257 return self
258
259
261 text = '%s instance at 0x%08X\n' % (self.__class__, id(self))
262
263
264 for attr in self._get_filtered_attribute_list():
265
266 attr_str_lines = str(
267 getattr(self, "_%s_value_" % attr.name)).splitlines()
268 if len(attr_str_lines) > 1:
269 text += '* %s :\n' % attr.name
270 for attr_str in attr_str_lines:
271 text += ' %s\n' % attr_str
272 else:
273 text += '* %s : %s\n' % (attr.name, attr_str_lines[0])
274 return text
275
276 - def read(self, stream, data):
283
285 """Set structure values from integer."""
286 bitpos = 0
287 for attr in self._get_filtered_attribute_list(data):
288
289 attrvalue = (value >> bitpos) & ((1 << attr.numbits) - 1)
290 setattr(self, attr.name, attrvalue)
291 bitpos += attr.numbits
292
294
295
296 """Get as integer."""
297 value = 0
298 bitpos = 0
299 for attr in self._get_filtered_attribute_list(data):
300 attrvalue = getattr(self, attr.name)
301 value |= (attrvalue & ((1 << attr.numbits) - 1)) << bitpos
302 bitpos += attr.numbits
303 return value
304
305 - def write(self, stream, data):
309
311 """Fix links in the structure."""
312 return
313
315 """Get list of all links in the structure."""
316 return []
317
319 """Get list of all strings in the structure."""
320 return []
321
323 """Get list of all references in the structure. Refs are
324 links that point down the tree. For instance, if you need to parse
325 the whole tree starting from the root you would use get_refs and not
326 get_links, as get_links could result in infinite recursion."""
327 return []
328
330 """Calculate the structure size in bytes."""
331 return self._numbytes
332
340
341 @classmethod
343 """Get games for which this block is supported."""
344 return list(cls._games.iterkeys())
345
346 @classmethod
348 """Get versions supported for C{game}."""
349 return cls._games[game]
350
351 @classmethod
353 """Calculate the list of all attributes of this structure."""
354
355 attrs = []
356 for base in cls.__bases__:
357 try:
358 attrs.extend(base._get_attribute_list())
359 except AttributeError:
360 pass
361 attrs.extend(cls._attrs)
362 return attrs
363
364 @classmethod
366 """Calculate the list of all attributes names in this structure.
367 Skips duplicate names."""
368
369 names = []
370
371
372
373
374
375 for attr in cls._attrs:
376 if attr.name in names:
377 continue
378 else:
379 names.append(attr.name)
380 return names
381
383 """Generator for listing all 'active' attributes, that is,
384 attributes whose condition evaluates ``True``, whose version
385 interval contaions C{version}, and whose user version is
386 C{user_version}. ``None`` for C{version} or C{user_version} means
387 that these checks are ignored. Duplicate names are skipped as
388 well.
389
390 Note: Use data instead of version and user_version (old way will be
391 deprecated)."""
392 names = []
393 if data:
394 version = data.version
395 user_version = data.user_version
396 else:
397 version = None
398 user_version = None
399 for attr in self._attribute_list:
400
401
402
403 if not (version is None):
404 if (not (attr.ver1 is None)) and version < attr.ver1:
405 continue
406 if (not (attr.ver2 is None)) and version > attr.ver2:
407 continue
408
409
410
411 if not(attr.userver is None or user_version is None) \
412 and user_version != attr.userver:
413 continue
414
415
416
417 if not (attr.cond is None) and not attr.cond.eval(self):
418 continue
419
420
421
422 if attr.name in names:
423 continue
424
425
426 names.append(attr.name)
427
428
429 yield attr
430
432 """Get a basic attribute."""
433 return getattr(self, "_" + name + "_value_").get_value()
434
435
436
438 """Set the value of a basic attribute."""
439 getattr(self, "_" + name + "_value_").set_value(value)
440
442 """A generator for parsing all blocks in the tree (starting from and
443 including C{self}). By default, there is no tree structure, so returns
444 self."""
445
446 yield self
447
448
449
451 """Yield children of this structure."""
452 return (item for item in self._items)
453
455 """Yield name of each child."""
456 return (name for name in self._names)
457