1 # Copyright 2019 The Pigweed Authors
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 # use this file except in compliance with the License. You may obtain a copy of
7 # https://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations under
14 """The binary_diff module defines a class which stores size diff information."""
19 from typing import List, Generator, Type
21 DiffSegment = collections.namedtuple(
22 'DiffSegment', ['name', 'before', 'after', 'delta', 'capacity'])
23 FormattedDiff = collections.namedtuple('FormattedDiff',
24 ['segment', 'before', 'delta', 'after'])
27 def format_integer(num: int, force_sign: bool = False) -> str:
28 """Formats a integer with commas."""
29 prefix = '+' if force_sign and num > 0 else ''
30 return '{}{:,}'.format(prefix, num)
33 def format_percent(num: float, force_sign: bool = False) -> str:
34 """Formats a decimal ratio as a percentage."""
35 prefix = '+' if force_sign and num > 0 else ''
36 return '{}{:,.1f}%'.format(prefix, num * 100)
40 """A size diff between two binary files."""
41 def __init__(self, label: str):
43 self._segments: collections.OrderedDict = collections.OrderedDict()
45 def add_segment(self, segment: DiffSegment):
46 """Adds a segment to the diff."""
47 self._segments[segment.name] = segment
49 def formatted_segments(self) -> Generator[FormattedDiff, None, None]:
50 """Yields each of the segments in this diff with formatted data."""
52 if not self._segments:
53 yield FormattedDiff('(all)', '(same)', '0', '(same)')
56 has_diff_segment = False
58 for segment in self._segments.values():
59 if segment.delta == 0:
62 has_diff_segment = True
65 format_integer(segment.before),
66 format_integer(segment.delta, force_sign=True),
67 format_integer(segment.after),
70 if not has_diff_segment:
71 yield FormattedDiff('(all)', '(same)', '0', '(same)')
74 def from_csv(cls: Type['BinaryDiff'], label: str,
75 raw_csv: List[str]) -> 'BinaryDiff':
76 """Parses a BinaryDiff from bloaty's CSV output."""
79 reader = csv.reader(raw_csv)
82 DiffSegment(row[0], int(row[5]), int(row[7]), int(row[1]),