Miasm2
 All Classes Namespaces Files Functions Variables Typedefs Properties Macros
interval.py
Go to the documentation of this file.
1 INT_EQ = 0 # Equivalent
2 INT_B_IN_A = 1 # B in A
3 INT_A_IN_B = -1 # A in B
4 INT_DISJOIN = 2 # Disjoint
5 INT_JOIN = 3 # Overlap
6 INT_JOIN_AB = 4 # B starts at the end of A
7 INT_JOIN_BA = 5 # A starts at the end of B
8 
9 
10 def cmp_interval(inter1, inter2):
11  """Compare @inter1 and @inter2 and returns the associated INT_* case
12  @inter1, @inter2: interval instance
13  """
14  if inter1 == inter2:
15  return INT_EQ
16 
17  inter1_start, inter1_stop = inter1
18  inter2_start, inter2_stop = inter2
19  result = INT_JOIN
20  if inter1_start <= inter2_start and inter1_stop >= inter2_stop:
21  result = INT_B_IN_A
22  if inter2_start <= inter1_start and inter2_stop >= inter1_stop:
23  result = INT_A_IN_B
24  if inter1_stop + 1 == inter2_start:
25  result = INT_JOIN_AB
26  if inter2_stop + 1 == inter1_start:
27  result = INT_JOIN_BA
28  if inter1_start > inter2_stop + 1 or inter2_start > inter1_stop + 1:
29  result = INT_DISJOIN
30  return result
31 
32 
34  """Stands for intervals with integer bounds
35 
36  Offers common methods to work with interval"""
37 
38  def __init__(self, bounds=None):
39  """Instance an interval object
40  @bounds: (optional) list of (int, int) and/or interval instance
41  """
42  if bounds is None:
43  bounds = []
44  elif isinstance(bounds, interval):
45  bounds = bounds.intervals
46  self.is_cannon = False
47  self.intervals = bounds
48  self.cannon()
49 
50  def __iter__(self):
51  """Iterate on intervals"""
52  for inter in self.intervals:
53  yield inter
54 
55  @staticmethod
56  def cannon_list(tmp):
57  """
58  Return a cannonizes list of intervals
59  @tmp: list of (int, int)
60  """
61  tmp = sorted([x for x in tmp if x[0] <= x[1]])
62  out = []
63  if not tmp:
64  return out
65  out.append(tmp.pop())
66  while tmp:
67  x = tmp.pop()
68  rez = cmp_interval(out[-1], x)
69 
70  if rez == INT_EQ:
71  continue
72  elif rez == INT_DISJOIN:
73  out.append(x)
74  elif rez == INT_B_IN_A:
75  continue
76  elif rez in [INT_JOIN, INT_JOIN_AB, INT_JOIN_BA, INT_A_IN_B]:
77  u, v = x
78  while out and cmp_interval(out[-1], (u, v)) in [
79  INT_JOIN, INT_JOIN_AB, INT_JOIN_BA, INT_A_IN_B]:
80  u = min(u, out[-1][0])
81  v = max(v, out[-1][1])
82  out.pop()
83  out.append((u, v))
84  else:
85  raise ValueError('unknown state', rez)
86  return out[::-1]
87 
88  def cannon(self):
89  "Apply .cannon_list() on self contained intervals"
90  if self.is_cannon is True:
91  return
92  self.intervals = interval.cannon_list(self.intervals)
93  self.is_cannon = True
94 
95  def __repr__(self):
96  if self.intervals:
97  o = " U ".join(["[0x%X 0x%X]" % (x[0], x[1])
98  for x in self.intervals])
99  else:
100  o = "[]"
101  return o
102 
103  def __contains__(self, other):
104  if isinstance(other, interval):
105  for intervalB in other.intervals:
106  is_in = False
107  for intervalA in self.intervals:
108  if cmp_interval(intervalA, intervalB) in [INT_EQ, INT_B_IN_A]:
109  is_in = True
110  break
111  if not is_in:
112  return False
113  return True
114  else:
115  for intervalA in self.intervals:
116  if intervalA[0] <= other <= intervalA[1]:
117  return True
118  return False
119 
120  def __eq__(self, i):
121  return self.intervals == i.intervals
122 
123  def __add__(self, i):
124  if isinstance(i, interval):
125  i = i.intervals
126  i = interval(self.intervals + i)
127  return i
128 
129  def __sub__(self, v):
130  to_test = self.intervals[:]
131  i = -1
132  to_del = v.intervals[:]
133  while i < len(to_test) - 1:
134  i += 1
135  x = to_test[i]
136  if x[0] > x[1]:
137  del to_test[i]
138  i -= 1
139  continue
140 
141  while to_del and to_del[0][1] < x[0]:
142  del to_del[0]
143 
144  for y in to_del:
145  if y[0] > x[1]:
146  break
147  rez = cmp_interval(x, y)
148  if rez == INT_DISJOIN:
149  continue
150  elif rez == INT_EQ:
151  del to_test[i]
152  i -= 1
153  break
154  elif rez == INT_A_IN_B:
155  del to_test[i]
156  i -= 1
157  break
158  elif rez == INT_B_IN_A:
159  del to_test[i]
160  i1 = (x[0], y[0] - 1)
161  i2 = (y[1] + 1, x[1])
162  to_test[i:i] = [i1, i2]
163  i -= 1
164  break
165  elif rez in [INT_JOIN_AB, INT_JOIN_BA]:
166  continue
167  elif rez == INT_JOIN:
168  del to_test[i]
169  if x[0] < y[0]:
170  to_test[i:i] = [(x[0], y[0] - 1)]
171  else:
172  to_test[i:i] = [(y[1] + 1, x[1])]
173  i -= 1
174  break
175  else:
176  raise ValueError('unknown state', rez)
177  return interval(to_test)
178 
179  def __and__(self, v):
180  out = []
181  for x in self.intervals:
182  if x[0] > x[1]:
183  continue
184  for y in v.intervals:
185  rez = cmp_interval(x, y)
186 
187  if rez == INT_DISJOIN:
188  continue
189  elif rez == INT_EQ:
190  out.append(x)
191  continue
192  elif rez == INT_A_IN_B:
193  out.append(x)
194  continue
195  elif rez == INT_B_IN_A:
196  out.append(y)
197  continue
198  elif rez == INT_JOIN_AB:
199  continue
200  elif rez == INT_JOIN_BA:
201  continue
202  elif rez == INT_JOIN:
203  if x[0] < y[0]:
204  out.append((y[0], x[1]))
205  else:
206  out.append((x[0], y[1]))
207  continue
208  else:
209  raise ValueError('unknown state', rez)
210  return interval(out)
211 
212  def hull(self):
213  "Return the first and the last bounds of intervals"
214  if not self.intervals:
215  return None, None
216  return self.intervals[0][0], self.intervals[-1][1]
217 
218 
219  @property
220  def empty(self):
221  """Return True iff the interval is empty"""
222  return not self.intervals
223 
224  def show(self, img_x=1350, img_y=20, dry_run=False):
225  """
226  show image representing the interval
227  """
228  try:
229  import Image
230  import ImageDraw
231  except ImportError:
232  print 'cannot import python PIL imaging'
233  return
234 
235  img = Image.new('RGB', (img_x, img_y), (100, 100, 100))
236  draw = ImageDraw.Draw(img)
237  i_min, i_max = self.hull()
238 
239  print hex(i_min), hex(i_max)
240 
241  addr2x = lambda addr: (addr - i_min) * img_x / (i_max - i_min)
242  for a, b in self.intervals:
243  draw.rectangle((addr2x(a), 0, addr2x(b), img_y), (200, 0, 0))
244 
245  if dry_run is False:
246  img.show()