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