| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright (C) 2017-2020 The Project X-Ray Authors. |
| # |
| # Use of this source code is governed by a ISC-style |
| # license that can be found in the LICENSE file or at |
| # https://opensource.org/licenses/ISC |
| # |
| # SPDX-License-Identifier: ISC |
| """ |
| This is a db fixup script that does two things: |
| |
| 1. Clears (removes) all bits found in "IN_USE" tag(s) and removes the IN_USE |
| tag itself |
| |
| 2. Reads tag group definition from a file and applies the tag grouping. First |
| a set of common bits for each group is found (logical OR among all tags |
| belonging to the group). Then in each tag belonging to the group those |
| bits are set to 0 but only if they are not already present there. |
| |
| The resulting data is written into a segbits file. |
| """ |
| import argparse |
| import re |
| import itertools |
| |
| # ============================================================================= |
| |
| |
| def load_tag_groups(file_name): |
| """ |
| Loads tag groups from a text file. |
| |
| A tag group is defined by specifying a space separated list of tags within |
| a single line. Lines that are empty or start with '#' are ignored. |
| """ |
| tag_groups = [] |
| |
| # Load tag group specifications |
| with open(file_name, "r") as fp: |
| for line in fp: |
| line = line.strip() |
| |
| if len(line) == 0 or line.startswith("#"): |
| continue |
| |
| group = set(line.split()) |
| if len(group): |
| tag_groups.append(group) |
| |
| # Check if all tag groups are exclusive |
| for tag_group_a, tag_group_b in itertools.combinations(tag_groups, 2): |
| |
| tags = tag_group_a & tag_group_b |
| if len(tags): |
| raise RuntimeError( |
| "Tag(s) {} are present in multiple groups".format( |
| " ".join(tags))) |
| |
| return tag_groups |
| |
| |
| # ============================================================================= |
| |
| |
| def parse_bit(bit): |
| """ |
| Decodes string describing a bit. Returns a tuple (frame, bit, value) |
| """ |
| match = re.match("^(!?)([0-9]+)_([0-9]+)$", bit) |
| assert match != None, bit |
| |
| val = int(match.group(1) != "!") |
| frm = int(match.group(2)) |
| bit = int(match.group(3)) |
| |
| return frm, bit, val |
| |
| |
| def bit_to_str(bit): |
| """ |
| Converts a tuple (frame, bit, value) to its string representation. |
| """ |
| s = "!" if not bit[2] else "" |
| return "{}{}_{:02d}".format(s, bit[0], bit[1]) |
| |
| |
| def load_segbits(file_name): |
| """ |
| Loads a segbits file. |
| """ |
| |
| segbits = {} |
| |
| with open(file_name, "r") as fp: |
| for line in fp: |
| line = line.strip() |
| fields = line.split() |
| |
| if len(fields) < 2: |
| raise RuntimeError("Malformed line: '%s'" % line) |
| |
| tag = fields[0] |
| |
| if "<" in line or ">" in line: |
| segbits[tag] = set() |
| |
| else: |
| bits = set([parse_bit(bit) for bit in fields[1:]]) |
| segbits[tag] = bits |
| |
| return segbits |
| |
| |
| def save_segbits(file_name, segbits): |
| """ |
| Save segbits to a .db or .rdb file |
| """ |
| |
| with open(file_name, "w") as fp: |
| for tag, bits in segbits.items(): |
| if not len(bits): |
| continue |
| |
| line = tag + " " |
| line += " ".join([bit_to_str(bit) for bit in sorted(list(bits))]) |
| fp.write(line + "\n") |
| |
| |
| # ============================================================================= |
| |
| |
| def mask_out_bits(segbits, mask, tags_to_mask=None): |
| """ |
| Given a set of bits and a list of tags to affect (optional) removes all |
| the bits from each tag that are present (and equal) in the masking set. |
| """ |
| |
| if tags_to_mask is None: |
| tags_to_mask = segbits.keys() |
| |
| # Mask out matching bits |
| for tag in tags_to_mask: |
| bits = segbits[tag] |
| |
| bits = set(bits) - set(mask) |
| segbits[tag] = bits |
| |
| return segbits |
| |
| |
| def find_common_bits_for_tag_groups(segbits, tag_groups): |
| """ |
| For each tag group finds a common set of bits that have value of one. |
| """ |
| |
| bit_groups = [] |
| |
| for tag_group in tag_groups: |
| bit_group = set() |
| |
| for tag, bits in segbits.items(): |
| if tag in tag_group: |
| ones = set([b for b in bits if b[2]]) |
| bit_group |= ones |
| |
| bit_groups.append(bit_group) |
| |
| return bit_groups |
| |
| |
| def group_tags(segbits, tag_groups, bit_groups): |
| """ |
| Implements tag grouping. If a tag belongs to a group then the common bits |
| of that group are added to is as zeros. |
| """ |
| |
| for tag_group, bit_group in zip(tag_groups, bit_groups): |
| zeros = set([(b[0], b[1], 0) for b in bit_group]) |
| for tag in tag_group: |
| |
| # Insert zero bits |
| if tag in segbits.keys(): |
| bits = segbits[tag] |
| for z in zeros: |
| if not (z[0], z[1], 1) in bits: |
| bits.add(z) |
| |
| return segbits |
| |
| |
| # ============================================================================= |
| |
| |
| def main(): |
| |
| # Parse arguments |
| parser = argparse.ArgumentParser() |
| |
| parser.add_argument("-i", required=True, type=str, help="Input .rdb file") |
| parser.add_argument( |
| "-g", required=True, type=str, help="Input tag group definition file") |
| parser.add_argument("-o", required=True, type=str, help="Output .rdb file") |
| |
| args = parser.parse_args() |
| |
| # Load tag groups |
| tag_groups = load_tag_groups(args.g) |
| |
| # Load raw database file |
| segbits = load_segbits(args.i) |
| |
| # Mask out bits present in "IN_USE" tag(s) as they are common to other tags |
| for tag in segbits.keys(): |
| if tag.endswith("IN_USE"): |
| prefix = tag[:tag.index("IN_USE")] |
| tags_to_mask = [t for t in segbits.keys() if t.startswith(prefix)] |
| mask_out_bits(segbits, segbits[tag], tags_to_mask) |
| |
| tags_to_remove = set() |
| for tag, bits in segbits.items(): |
| if len(bits) == 0: |
| tags_to_remove.add(tag) |
| |
| for tag in tags_to_remove: |
| del segbits[tag] |
| |
| for tag in segbits.keys(): |
| if tag.endswith("_ACTIVE") and 'FREQ_BB' in tag: |
| m = re.search('FREQ_BB([0-9])', tag) |
| l_prefix = '.CMT_TOP_L_UPPER_T_FREQ_BB{}'.format(m.group(1)) |
| r_prefix = '.CMT_TOP_R_UPPER_T_FREQ_BB{}'.format(m.group(1)) |
| |
| l_tags_to_mask = [t for t in segbits.keys() if t.endswith(l_prefix)] |
| r_tags_to_mask = [t for t in segbits.keys() if t.endswith(r_prefix)] |
| |
| mask_out_bits(segbits, segbits[tag], l_tags_to_mask) |
| mask_out_bits(segbits, segbits[tag], r_tags_to_mask) |
| |
| # Find common bits |
| bit_groups = find_common_bits_for_tag_groups(segbits, tag_groups) |
| # Apply tag grouping |
| segbits = group_tags(segbits, tag_groups, bit_groups) |
| |
| # Save fixed database file |
| save_segbits(args.o, segbits) |
| |
| |
| if __name__ == "__main__": |
| main() |