1#!/usr/bin/env python3 2# 3# Copyright (c) 2017 Intel Corporation 4# 5# SPDX-License-Identifier: Apache-2.0 6 7""" 8gperf C file post-processor 9 10We use gperf to build up a perfect hashtable of pointer values. The way gperf 11does this is to create a table 'wordlist' indexed by a string representation 12of a pointer address, and then doing memcmp() on a string passed in for 13comparison 14 15We are exclusively working with 4-byte pointer values. This script adjusts 16the generated code so that we work with pointers directly and not strings. 17This saves a considerable amount of space. 18""" 19 20import argparse 21import os 22import re 23import sys 24 25from packaging import version 26 27# --- debug stuff --- 28 29 30def debug(text): 31 if not args.verbose: 32 return 33 sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") 34 35 36def error(text): 37 sys.exit(os.path.basename(sys.argv[0]) + " ERROR: " + text) 38 39 40def warn(text): 41 sys.stdout.write(os.path.basename(sys.argv[0]) + " WARNING: " + text + "\n") 42 43 44def reformat_str(match_obj): 45 addr_str = match_obj.group(0) 46 47 # Nip quotes 48 addr_str = addr_str[1:-1] 49 addr_vals = [0, 0, 0, 0, 0, 0, 0, 0] 50 ctr = 7 51 i = 0 52 53 while True: 54 if i >= len(addr_str): 55 break 56 57 if addr_str[i] == "\\": 58 if addr_str[i + 1].isdigit(): 59 # Octal escape sequence 60 val_str = addr_str[i + 1 : i + 4] 61 addr_vals[ctr] = int(val_str, 8) 62 i += 4 63 else: 64 # Char value that had to be escaped by C string rules 65 addr_vals[ctr] = ord(addr_str[i + 1]) 66 i += 2 67 68 else: 69 addr_vals[ctr] = ord(addr_str[i]) 70 i += 1 71 72 ctr -= 1 73 74 return f"(char *)0x{bytes(addr_vals).hex()}" 75 76 77def process_line(line, fp): 78 if line.startswith("#"): 79 fp.write(line) 80 return 81 82 # Set the lookup function to static inline so it gets rolled into 83 # k_object_find(), nothing else will use it 84 if re.search(args.pattern + " [*]$", line): 85 fp.write("static inline " + line) 86 return 87 88 m = re.search("gperf version (.*) [*][/]$", line) 89 if m: 90 v = version.parse(m.groups()[0]) 91 v_lo = version.parse("3.0") 92 v_hi = version.parse("3.1") 93 if v < v_lo or v > v_hi: 94 warn(f"gperf {v} is not tested, versions {v_lo} through {v_hi} supported") 95 96 # Replace length lookups with constant len since we're always 97 # looking at pointers 98 line = re.sub(r'lengthtable\[key\]', r'sizeof(void *)', line) 99 100 # Empty wordlist entries to have NULLs instead of "" 101 line = re.sub(r'[{]["]["][}]', r'{}', line) 102 103 # Suppress a compiler warning since this table is no longer necessary 104 line = re.sub( 105 r'static unsigned char lengthtable', r'static unsigned char __unused lengthtable', line 106 ) 107 108 # drop all use of register keyword, let compiler figure that out, 109 # we have to do this since we change stuff to take the address of some 110 # parameters 111 line = re.sub(r'register', r'', line) 112 113 # Hashing the address of the string 114 line = re.sub(r"hash [(]str, len[)]", r"hash((const char *)&str, len)", line) 115 116 # Just compare pointers directly instead of using memcmp 117 if re.search("if [(][*]str", line): 118 fp.write(" if (str == s)\n") 119 return 120 121 # Take the strings with the binary information for the pointer values, 122 # and just turn them into pointers 123 line = re.sub(r'["].*["]', reformat_str, line) 124 125 # Use a bigger data type for the asso_values table to provide some margin 126 line = re.sub(r'char asso_values', r'short asso_values', line) 127 128 fp.write(line) 129 130 131def parse_args(): 132 global args 133 134 parser = argparse.ArgumentParser( 135 description=__doc__, 136 formatter_class=argparse.RawDescriptionHelpFormatter, 137 allow_abbrev=False, 138 ) 139 140 parser.add_argument("-i", "--input", required=True, help="Input C file from gperf") 141 parser.add_argument("-o", "--output", required=True, help="Output C file with processing done") 142 parser.add_argument("-p", "--pattern", required=True, help="Search pattern for objects") 143 parser.add_argument( 144 "-v", "--verbose", action="store_true", help="Print extra debugging information" 145 ) 146 args = parser.parse_args() 147 if "VERBOSE" in os.environ: 148 args.verbose = 1 149 150 151def main(): 152 parse_args() 153 154 with open(args.input) as in_fp, open(args.output, "w") as out_fp: 155 for line in in_fp.readlines(): 156 process_line(line, out_fp) 157 158 159if __name__ == "__main__": 160 main() 161