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 distutils.version import LooseVersion
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    # z_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 = LooseVersion(m.groups()[0])
94        v_lo = LooseVersion("3.0")
95        v_hi = LooseVersion("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
139    parser.add_argument("-i", "--input", required=True,
140                        help="Input C file from gperf")
141    parser.add_argument("-o", "--output", required=True,
142                        help="Output C file with processing done")
143    parser.add_argument("-p", "--pattern", required=True,
144            help="Search pattern for objects")
145    parser.add_argument("-v", "--verbose", action="store_true",
146                        help="Print extra debugging information")
147    args = parser.parse_args()
148    if "VERBOSE" in os.environ:
149        args.verbose = 1
150
151def main():
152    parse_args()
153
154    with open(args.input, "r") 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