Clean up whitespace
[pyelftools.git] / elftools / common / construct_utils.py
1 #-------------------------------------------------------------------------------
2 # elftools: common/construct_utils.py
3 #
4 # Some complementary construct utilities
5 #
6 # Eli Bendersky (eliben@gmail.com)
7 # This code is in the public domain
8 #-------------------------------------------------------------------------------
9 from ..construct import (
10 Subconstruct, ConstructError, ArrayError, Adapter, Field, RepeatUntil,
11 Rename, SizeofError, Construct
12 )
13
14
15 class RepeatUntilExcluding(Subconstruct):
16 """ A version of construct's RepeatUntil that doesn't include the last
17 element (which casued the repeat to exit) in the return value.
18
19 Only parsing is currently implemented.
20
21 P.S. removed some code duplication
22 """
23 __slots__ = ["predicate"]
24 def __init__(self, predicate, subcon):
25 Subconstruct.__init__(self, subcon)
26 self.predicate = predicate
27 self._clear_flag(self.FLAG_COPY_CONTEXT)
28 self._set_flag(self.FLAG_DYNAMIC)
29 def _parse(self, stream, context):
30 obj = []
31 try:
32 context_for_subcon = context
33 if self.subcon.conflags & self.FLAG_COPY_CONTEXT:
34 context_for_subcon = context.__copy__()
35
36 while True:
37 subobj = self.subcon._parse(stream, context_for_subcon)
38 if self.predicate(subobj, context):
39 break
40 obj.append(subobj)
41 except ConstructError as ex:
42 raise ArrayError("missing terminator", ex)
43 return obj
44 def _build(self, obj, stream, context):
45 raise NotImplementedError('no building')
46 def _sizeof(self, context):
47 raise SizeofError("can't calculate size")
48
49
50 def _LEB128_reader():
51 """ Read LEB128 variable-length data from the stream. The data is terminated
52 by a byte with 0 in its highest bit.
53 """
54 return RepeatUntil(
55 lambda obj, ctx: ord(obj) < 0x80,
56 Field(None, 1))
57
58
59 class _ULEB128Adapter(Adapter):
60 """ An adapter for ULEB128, given a sequence of bytes in a sub-construct.
61 """
62 def _decode(self, obj, context):
63 value = 0
64 for b in reversed(obj):
65 value = (value << 7) + (ord(b) & 0x7F)
66 return value
67
68
69 class _SLEB128Adapter(Adapter):
70 """ An adapter for SLEB128, given a sequence of bytes in a sub-construct.
71 """
72 def _decode(self, obj, context):
73 value = 0
74 for b in reversed(obj):
75 value = (value << 7) + (ord(b) & 0x7F)
76 if ord(obj[-1]) & 0x40:
77 # negative -> sign extend
78 value |= - (1 << (7 * len(obj)))
79 return value
80
81
82 def ULEB128(name):
83 """ A construct creator for ULEB128 encoding.
84 """
85 return Rename(name, _ULEB128Adapter(_LEB128_reader()))
86
87
88 def SLEB128(name):
89 """ A construct creator for SLEB128 encoding.
90 """
91 return Rename(name, _SLEB128Adapter(_LEB128_reader()))
92
93 class StreamOffset(Construct):
94 """
95 Captures the current stream offset
96
97 Parameters:
98 * name - the name of the value
99
100 Example:
101 StreamOffset("item_offset")
102 """
103 __slots__ = []
104 def __init__(self, name):
105 Construct.__init__(self, name)
106 self._set_flag(self.FLAG_DYNAMIC)
107 def _parse(self, stream, context):
108 return stream.tell()
109 def _build(self, obj, stream, context):
110 context[self.name] = stream.tell()
111 def _sizeof(self, context):
112 return 0