| # Copyright 2019 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| """The binary_diff module defines a class which stores size diff information.""" |
| |
| import collections |
| import csv |
| |
| from typing import List, Generator, Type |
| |
| DiffSegment = collections.namedtuple( |
| 'DiffSegment', ['name', 'before', 'after', 'delta', 'capacity']) |
| FormattedDiff = collections.namedtuple('FormattedDiff', |
| ['segment', 'before', 'delta', 'after']) |
| |
| |
| def format_integer(num: int, force_sign: bool = False) -> str: |
| """Formats a integer with commas.""" |
| prefix = '+' if force_sign and num > 0 else '' |
| return '{}{:,}'.format(prefix, num) |
| |
| |
| def format_percent(num: float, force_sign: bool = False) -> str: |
| """Formats a decimal ratio as a percentage.""" |
| prefix = '+' if force_sign and num > 0 else '' |
| return '{}{:,.1f}%'.format(prefix, num * 100) |
| |
| |
| class BinaryDiff: |
| """A size diff between two binary files.""" |
| def __init__(self, label: str): |
| self.label = label |
| self._segments: collections.OrderedDict = collections.OrderedDict() |
| |
| def add_segment(self, segment: DiffSegment): |
| """Adds a segment to the diff.""" |
| self._segments[segment.name] = segment |
| |
| def formatted_segments(self) -> Generator[FormattedDiff, None, None]: |
| """Yields each of the segments in this diff with formatted data.""" |
| |
| if not self._segments: |
| yield FormattedDiff('(all)', '(same)', '0', '(same)') |
| return |
| |
| has_diff_segment = False |
| |
| for segment in self._segments.values(): |
| if segment.delta == 0: |
| continue |
| |
| has_diff_segment = True |
| yield FormattedDiff( |
| segment.name, |
| format_integer(segment.before), |
| format_integer(segment.delta, force_sign=True), |
| format_integer(segment.after), |
| ) |
| |
| if not has_diff_segment: |
| yield FormattedDiff('(all)', '(same)', '0', '(same)') |
| |
| @classmethod |
| def from_csv(cls: Type['BinaryDiff'], label: str, |
| raw_csv: List[str]) -> 'BinaryDiff': |
| """Parses a BinaryDiff from bloaty's CSV output.""" |
| |
| diff = cls(label) |
| reader = csv.reader(raw_csv) |
| for row in reader: |
| diff.add_segment( |
| DiffSegment(row[0], int(row[5]), int(row[7]), int(row[1]), |
| int(row[3]))) |
| |
| return diff |