1
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 Core Pike infrastructure
39 """
40
41 import array
42 import struct
43 import inspect
46 """Buffer overrun exception"""
47 pass
48
50 """
51 Byte array cursor
52
53 Represents a position within a byte array.
54
55 encode_* operations write data at the current position
56 and advance it by the number of bytes written.
57
58 decode_* operations read data at the current position,
59 advance it by the number of bytes read, and return
60 the result.
61
62 Cursors support in-place addition and subtraction (+=,-=) of
63 integer values to manually adjust position, as well
64 as ordinary addition and subtraction, which return a new
65 cursor without modifiying the original.
66
67 Ordinary subtraction of two cursors will yield the
68 difference between their positions as an integer.
69 ==, !=, <, <=, >, and >= operators will also work to
70 compare positions. The results will only be meaningful
71 for cursors referencing the same underlying array.
72
73 For a given cursor, cursor.hole.encode_* will perform
74 the same operation as cursor.encode_*, but will return
75 a hole object. Calling this hole object with the same
76 type of arguments as the original encode method will
77 overwrite the original value. This mechanism is useful
78 for backpatching values into fields that aren't known until
79 later, such as internal packet lengths and offsets or
80 checksum fields. For example::
81
82 hole = cursor.hole.encode_uint32le(0)
83 # Encode rest of packet
84 ...
85 # Overwrite value with calculated checksum
86 hole(sum)
87
88 Cursors support slicing to extract sections
89 of the underlying array. For example::
90
91 # Extract array slice between cur1 and cur2
92 subarray = cur1[:cur2]
93
94 Cursors also support establishing boundaries outside of which
95 decoding will raise exceptions::
96
97 with cur.bounded(startcur, endcur):
98 # Within this block, attempts to decode data outside of
99 # the range starting with startcur (inclusive) and ending
100 # with endcur (exclusive) will raise BufferOverrun().
101 # If the start and end paremeters are numbers rather than
102 # other cursors, they will be taken to be relative to
103 # cur itself.
104 ...
105
106 @ivar array: Array referenced by cursor
107 @ivar offset: Offset within the array
108 @ivar bounds: Pair of lower and upper bound on offset
109 """
110
111 - def __init__(self, arr, offset, bounds=(None,None)):
112 """
113 Create a L{Cursor} for the given array
114 at the given offset.
115
116 @type arr: array.array('B', ...)
117 @param arr: The array
118 @type offset: number
119 @param offset: The offset from the start of the array
120 @param bounds: A pair of a lower and upper bound on valid offsets
121 """
122
123 self.array = arr
124 self.offset = offset
125 self.bounds = bounds
126 self.hole = Cursor.Hole(self)
127
129 return self.array is o.array and self.offset == o.offset
130
132 return not (self == o)
133
135 assert self.array is o.array
136 return self.offset < o.offset
137
139 assert self.array is o.array
140 return self.offset > o.offset
141
143 assert self.array is o.array
144 return self.offset <= o.offset
145
147 assert self.array is o.array
148 return self.offset >= o.offset
149
151 return Cursor(self.array, self.offset + o, self.bounds)
152
154 if isinstance(o, Cursor):
155 assert self.array is o.array
156 return self.offset - o.offset
157 else:
158 return Cursor(self.array, self.offset - o, self.bounds)
159
161 self.offset += o
162 return self
163
165 self.offset -= o
166 return self
167
169 if ind is None:
170 return None
171 elif isinstance(ind, Cursor):
172 assert self.array is ind.array
173 return ind.offset
174 else:
175 return self.offset + ind
176
188
190 return 'Cursor(' + object.__repr__(self.array) + ',' + repr(self.offset) + ',' + repr(self.bounds) + ')'
191
193 """ Create copy of cursor. """
194 return Cursor(self.array, self.offset, self.bounds)
195
197 cur_size = len(self.array)
198 if (size > cur_size):
199 self.array.extend([0]*(size - cur_size))
200
202 """ Encode bytes. Accepts byte arrays, strings, and integer lists."""
203 size = len(val)
204 self._expand_to(self.offset + size)
205 self.array[self.offset:self.offset + size] = array.array('B',val)
206 self.offset += size
207
209 size = struct.calcsize(fmt)
210 self._expand_to(self.offset + size)
211 struct.pack_into(fmt, self.array, self.offset, *args)
212 self.offset += size
213
216
219
222
225
228
231
234
237
240
243
245 self._expand_to(self.offset)
246 del self.array[self.offset:]
247
249 lower = self.bounds[0] if self.bounds[0] is not None else 0
250 upper = self.bounds[1] if self.bounds[1] is not None else len(self.array)
251
252 if start < lower or end > upper:
253 raise BufferOverrun()
254
256 self._check_bounds(self.offset, self.offset + size)
257 result = self.array[self.offset:self.offset+size]
258 self.offset += size
259 return result
260
262 size = struct.calcsize(fmt)
263 self._check_bounds(self.offset, self.offset + size)
264 result = struct.unpack_from(fmt, self.array, self.offset)
265 self.offset += size
266 return result
267
270
273
276
279
282
285
288
291
294
297
300
301 - def align(self, base, val):
302 assert self.array is base.array
303 rem = (self.offset - base.offset) % val
304 if rem != 0:
305 self.offset += val - rem
306
307 - def seekto(self, o, lowerbound = None, upperbound = None):
313
315 self.seekto(o, self, bound)
316
318 self.seekto(o, bound, self)
319
320 @property
322 lower = self.bounds[0] if self.bounds[0] is not None else 0
323 return Cursor(self.array, lower, self.bounds)
324
325 @property
327 upper = self.bounds[1] if self.bounds[1] is not None else len(self.array)
328 return Cursor(self.array, upper, self.bounds)
329
331
332 if isinstance(lower, Cursor):
333 assert self.array is lower.array
334 lower = lower.offset
335 else:
336 lower = cur.offset + lower
337 if isinstance(upper, Cursor):
338 assert self.array is upper.array
339 upper = upper.offset
340 else:
341 upper = cur.offset + upper
342
343
344 self._check_bounds(lower, upper)
345
346 return Cursor.Bounds(self, lower, upper)
347
348 - class Hole(object):
352 if hasattr(self.cur.__class__, attr):
353 f = getattr(self.cur.__class__, attr)
354 if inspect.ismethod(f):
355 copy = self.cur.copy()
356 def f2(*args, **kwargs):
357 offset = copy.offset
358 f(copy,*args,**kwargs)
359 copy.offset = offset
360
361 def f1(*args, **kwargs):
362 f(self.cur,*args,**kwargs)
363 return f2
364
365 return f1
366 else:
367 raise AttributeError
368 else:
369 raise AttributeError
370
373 self.cur = cur
374 self.bounds = (lower,upper)
375
377 self.oldbounds = self.cur.bounds
378 self.cur.bounds = self.bounds
379 return self
380
381 - def __exit__(self, exc_type, exc_value, traceback):
382 self.cur.bounds = self.oldbounds
383
386
388 field_blacklist = ['fields','parent','start','end']
389
390 - def __init__(self, parent, context=None):
391 object.__setattr__(self, 'fields', [])
392 self.parent = parent
393 self._context = context
394
397
400
403
410
413
415 if isinstance(value, array.array) and value.typecode == 'B':
416 return '0x' + ''.join(map(lambda b:'%.2x'%b,value))
417 else:
418 return str(value)
419
420 - def _str(self, indent):
421 res = self.__class__.__name__
422 for field in self.fields:
423 value = getattr(self, field)
424 if value is not None:
425 if isinstance(value, Frame):
426 valstr = value._str(indent + 1)
427 else:
428 valstr = self._value_str(value)
429 res += "\n" + " " * indent + field + ": " + valstr
430 for child in self.children:
431 valstr = child._str(indent + 1)
432 res += "\n" + " " * indent + valstr
433 return res
434
436 self.start = cur.copy()
437
438 - def _encode_post(self, cur):
439 self.end = cur.copy()
440
442 self.start = cur.copy()
443
444 - def _decode_post(self, cur):
445 self.end = cur.copy()
446
447 @property
449 if self._context is not None:
450 return self._context
451 elif self.parent is not None:
452 return self.parent.context
453 else:
454 return None
455
456 @property
458 return self._children() if hasattr(self, '_children') else []
459
464
469
471 self.buf = array.array('B')
472 cursor = Cursor(self.buf, 0)
473 self.encode(cursor)
474 return self.buf
475
477 cursor = Cursor(arr, 0)
478 self.decode(cursor)
479 self.buf = arr
480
485
490
494
522
525 self.table = table
526 self.keyattrs = keyattrs
527
529 assert issubclass(cls, Frame)
530 cls._register.append((self.table,self.keyattrs))
531 return cls
532
534 """
535 Enumeration abstract base
536
537 An Enum subclasses long in order to ensure that instances
538 are a member of a well-defined enumeration of values,
539 provide introspection into the allowed values and their names,
540 and provide symbolic string forms of values.
541
542 You should generally subclass one of L{ValueEnum} or L{FlagEnum}.
543 """
544
545 @classmethod
547 """
548 Returns a list of (name,value) pairs for allowed enumeration values.
549 """
550 return cls._nametoval.iteritems()
551
552 @classmethod
554 """
555 Returns a list of names of allowed enumeration values.
556 """
557 return [name for (name,value) in cls.items()]
558
559 @classmethod
561 """
562 Returns a list of allowed enumeration values.
563 """
564 return [value for (name,value) in cls.items()]
565
566 @classmethod
568 """
569 Import enumeration values into dictionary
570
571 This method is intended to allow importing enumeration
572 values from an L{Enum} subclass into the root of a module,
573 and should be invoked as::
574
575 SomeEnumClass.import_items(globals())
576 """
577 dictionary.update((name,cls(value)) for (name,value) in cls.items())
578
579 @classmethod
581 """
582 Validate value as valid for enumeration.
583
584 This must be implemented in subclasses of L{Enum}
585 (but not L{ValueEnum} or L{FlagEnum}, which provide it).
586 It should raise a ValueError on failure.
587 """
588 raise NotImplementedError()
589
591 """
592 Constructor.
593
594 Creates a new Enum instance from an ordinary number,
595 validating that is is valid for the particular
596 enumeration.
597 """
598 cls.validate(value)
599 return super(Enum, cls).__new__(cls, value)
600
604
630
632 """
633 Value Enumeration
634
635 Subclass this class to define enumerations of values.
636 For example::
637
638 class SomeEnumClass(Enum):
639 FOO = 0
640 BAR = 1
641 BAZ = 2
642
643 Accessing SomeEnumClass.FOO will actually return a SomeEnumClass instance.
644 Instances will return their symbolic names when str() is used.
645 """
646
647 permissive = False
648
649 @classmethod
651 if not cls.permissive and value not in cls._valtoname:
652 raise ValueError("Invalid %s: %x" % (cls.__name__, value))
653
655 if self in self.__class__._valtoname:
656 return self.__class__._valtoname[self]
657 else:
658 return hex(self)
659
661 """
662 Flag Enumeration
663
664 Subclass this class to define enumerations of flags.
665 For example::
666
667 class SomeFlagEnumClass(Enum):
668 FOO = 0x1
669 BAR = 0x2
670 BAZ = 0x4
671
672 Accessing SomeFlagEnumClass.FOO will actually return a
673 SomeFlagEnumClass instance. Instances may be bitwise-ored
674 together. Instances will return the symbolic names of all
675 set flags joined by ' | ' when str() is used.
676 """
677
678 @classmethod
680 remaining = value
681 for flag in cls.values():
682 if flag & remaining == flag:
683 remaining &= ~flag
684
685 if remaining != 0:
686 raise ValueError("Invalid %s: 0x%x (remainder 0x%x)" % (cls.__name__, value, remaining))
687
689 names = [name for (name,flag) in self.items()
690 if (flag != 0 and flag & self == flag) or
691 (self == 0 and flag == 0)]
692
693 return ' | '.join(names) if len(names) else '0'
694
697
700
702 """
703 A Let allows for temporarily storing passed arguments in the _settings
704 member of a target object for the duration of the contextmanager's scope
705
706 Implementation on a factory class
707
708 class MyThingFactory(object):
709 def __init__(self):
710 self._settings = {}
711 def generate_thing(self):
712 # do something here
713 new_obj = Thing()
714 for k,v in self._settings.iteritems():
715 setattr(new_obj, k, v)
716 return new_obj
717 def let(self, **kwds):
718 return Let(self, kwds)
719
720 Calling code that wants to temporarily customize the Thing objects that are
721 returned by the factory:
722
723 fac = MyThingFactory()
724 with fac.let(this="that", foo="bar"):
725 a_thing = fac.generate_thing()
726 self.assertEqual(a_thing.this, "that")
727 """
728 - def __init__(self, target, settings_dict):
729 self.target = target
730 self.settings_dict = settings_dict
731 if not hasattr(target, "_settings"):
732 target._settings = {}
733
735 self.backup = dict(self.target._settings)
736 self.target._settings.update(self.settings_dict)
737 return self
738
739 - def __exit__(self, exc_type, exc_val, exc_tb):
740 self.target._settings = self.backup
741