diff options
Diffstat (limited to 'plugins/python/cglimpse/entropy.py')
-rw-r--r-- | plugins/python/cglimpse/entropy.py | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/plugins/python/cglimpse/entropy.py b/plugins/python/cglimpse/entropy.py new file mode 100644 index 0000000..f090624 --- /dev/null +++ b/plugins/python/cglimpse/entropy.py @@ -0,0 +1,158 @@ + +import math + +from .method import GlimpseMethod + + +class ShannonEntropy(GlimpseMethod): + + def __init__(self, builder): + """Prepare a Shannon entropy display.""" + + super(ShannonEntropy, self).__init__(builder) + + button = builder.get_object('shannon_color') + button.connect('color-set', self._on_color_set) + + self._on_color_set(button) + + self._step = 0x80 + + self._v_legend = 'Entropy' + self._h_legend = 'Byte offsets' + + self._x_range = [ 0, 1024, 10240 ] + self._y_range = [ 0.0, 0.25, 1.0 ] + + self._size = None + self._values = [] + + + def _on_color_set(self, button): + """React on color chosen for the rendering.""" + + color = button.get_rgba() + self._color = [ color.red, color.green, color.blue, color.alpha ] + self._shadow_color = [ color.red * 0.5, color.green * 0.5, color.blue * 0.5, color.alpha ] + + + def format_legend(self, value, vert): + """Build the label used for a rule.""" + + if vert: + text = str(value) + + else: + + scale = [ ' kb', ' Mb', ' Gb', ' Tb' ] + suffix = '' + + for i in range(len(scale)): + + if value < 1024: + break + + value /= 1024 + suffix = scale[i] + + text = '%u%s' % (value, suffix) + + return text + + + def update(self, data): + """Provide a description for the method.""" + + self._size = len(data) + + step = 2 ** math.ceil(math.log(self._size / 10, 2)) + + self._x_range = [ 0, step, 10 * step ] + + self._values = [] + + for i in range(0, self._size, self._step): + + counter = [ 0 for i in range(256) ] + + start = i + end = i + self._step + + if end > self._size: + end = self._size + + for b in data[start : end]: + counter[b] += 1 + + ent = 0.0 + + for c in counter: + if c > 0: + freq = c / (end - start) + ent += freq * math.log(freq, 256) + + self._values.append(-ent) + + + def render(self, cr, area): + """Draw the bytes distribution for the current binary, if any.""" + + step = 2 ** math.ceil(math.log(self._size / 10, 2)) + + if self._size % step == 0: + full_size = self._size + else: + full_size = (self._size + step - 1) & ~(step - 1) + + start = 0 + last_x = area[0] + + last_y = area[1] + area[3] - (area[3] * self._values[0]) + cr.move_to(last_x, last_y + 2) + + for i in range(0, self._size, self._step): + + end = i + self._step + + if end > self._size: + end = self._size + + x = area[0] + ((end - start) * area[2]) / full_size + y = area[1] + area[3] - (area[3] * self._values[int(i / self._step)]) + + if last_y != y: + cr.line_to(last_x, y + 2) + + cr.line_to(x, y + 2) + + last_x = x + + cr.set_source_rgba(*self._shadow_color) + cr.set_line_width(4) + cr.stroke() + + last_x = area[0] + + last_y = area[1] + area[3] - (area[3] * self._values[0]) + cr.move_to(last_x, last_y) + + for i in range(0, self._size, self._step): + + end = i + self._step + + if end > self._size: + end = self._size + + x = area[0] + ((end - start) * area[2]) / full_size + y = area[1] + area[3] - (area[3] * self._values[int(i / self._step)]) + + if last_y != y: + cr.line_to(last_x, y) + + cr.line_to(x, y) + + last_x = x + + cr.set_source_rgba(*self._color) + cr.set_line_width(2) + cr.stroke() |