xref: /aosp_15_r20/external/libxkbcommon/scripts/perfect_hash.py (revision 2b949d0487e80d67f1fda82db69e101e761f8064)
1*2b949d04SAndroid Build Coastguard Worker# Derived from: https://github.com/ilanschnell/perfect-hash
2*2b949d04SAndroid Build Coastguard Worker# Commit: 6b7dd80a525dbd4349ea2c69f04a9c96f3c2fd54
3*2b949d04SAndroid Build Coastguard Worker
4*2b949d04SAndroid Build Coastguard Worker# BSD 3-Clause License
5*2b949d04SAndroid Build Coastguard Worker#
6*2b949d04SAndroid Build Coastguard Worker# Copyright (c) 2019 - 2021, Ilan Schnell
7*2b949d04SAndroid Build Coastguard Worker# All rights reserved.
8*2b949d04SAndroid Build Coastguard Worker#
9*2b949d04SAndroid Build Coastguard Worker# Redistribution and use in source and binary forms, with or without
10*2b949d04SAndroid Build Coastguard Worker# modification, are permitted provided that the following conditions are met:
11*2b949d04SAndroid Build Coastguard Worker#     * Redistributions of source code must retain the above copyright
12*2b949d04SAndroid Build Coastguard Worker#       notice, this list of conditions and the following disclaimer.
13*2b949d04SAndroid Build Coastguard Worker#     * Redistributions in binary form must reproduce the above copyright
14*2b949d04SAndroid Build Coastguard Worker#       notice, this list of conditions and the following disclaimer in the
15*2b949d04SAndroid Build Coastguard Worker#       documentation and/or other materials provided with the distribution.
16*2b949d04SAndroid Build Coastguard Worker#     * Neither the name of the Ilan Schnell nor the
17*2b949d04SAndroid Build Coastguard Worker#       names of its contributors may be used to endorse or promote products
18*2b949d04SAndroid Build Coastguard Worker#       derived from this software without specific prior written permission.
19*2b949d04SAndroid Build Coastguard Worker#
20*2b949d04SAndroid Build Coastguard Worker# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21*2b949d04SAndroid Build Coastguard Worker# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22*2b949d04SAndroid Build Coastguard Worker# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23*2b949d04SAndroid Build Coastguard Worker# DISCLAIMED. IN NO EVENT SHALL ILAN SCHNELL BE LIABLE FOR ANY
24*2b949d04SAndroid Build Coastguard Worker# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25*2b949d04SAndroid Build Coastguard Worker# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26*2b949d04SAndroid Build Coastguard Worker# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27*2b949d04SAndroid Build Coastguard Worker# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28*2b949d04SAndroid Build Coastguard Worker# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29*2b949d04SAndroid Build Coastguard Worker# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30*2b949d04SAndroid Build Coastguard Worker
31*2b949d04SAndroid Build Coastguard Worker"""
32*2b949d04SAndroid Build Coastguard WorkerGenerate a minimal perfect hash function for the keys in a file,
33*2b949d04SAndroid Build Coastguard Workerdesired hash values may be specified within this file as well.
34*2b949d04SAndroid Build Coastguard WorkerA given code template is filled with parameters, such that the
35*2b949d04SAndroid Build Coastguard Workeroutput is code which implements the hash function.
36*2b949d04SAndroid Build Coastguard WorkerTemplates can easily be constructed for any programming language.
37*2b949d04SAndroid Build Coastguard Worker
38*2b949d04SAndroid Build Coastguard WorkerThe code is based on an a program A.M. Kuchling wrote:
39*2b949d04SAndroid Build Coastguard Workerhttp://www.amk.ca/python/code/perfect-hash
40*2b949d04SAndroid Build Coastguard Worker
41*2b949d04SAndroid Build Coastguard WorkerThe algorithm the program uses is described in the paper
42*2b949d04SAndroid Build Coastguard Worker'Optimal algorithms for minimal perfect hashing',
43*2b949d04SAndroid Build Coastguard WorkerZ. J. Czech, G. Havas and B.S. Majewski.
44*2b949d04SAndroid Build Coastguard Workerhttp://citeseer.ist.psu.edu/122364.html
45*2b949d04SAndroid Build Coastguard Worker
46*2b949d04SAndroid Build Coastguard WorkerThe algorithm works like this:
47*2b949d04SAndroid Build Coastguard Worker
48*2b949d04SAndroid Build Coastguard Worker1.  You have K keys, that you want to perfectly hash against some
49*2b949d04SAndroid Build Coastguard Worker    desired hash values.
50*2b949d04SAndroid Build Coastguard Worker
51*2b949d04SAndroid Build Coastguard Worker2.  Choose a number N larger than K.  This is the number of
52*2b949d04SAndroid Build Coastguard Worker    vertices in a graph G, and also the size of the resulting table G.
53*2b949d04SAndroid Build Coastguard Worker
54*2b949d04SAndroid Build Coastguard Worker3.  Pick two random hash functions f1, f2, that return values from 0..N-1.
55*2b949d04SAndroid Build Coastguard Worker
56*2b949d04SAndroid Build Coastguard Worker4.  Now, for all keys, you draw an edge between vertices f1(key) and f2(key)
57*2b949d04SAndroid Build Coastguard Worker    of the graph G, and associate the desired hash value with that edge.
58*2b949d04SAndroid Build Coastguard Worker
59*2b949d04SAndroid Build Coastguard Worker5.  If G is cyclic, go back to step 2.
60*2b949d04SAndroid Build Coastguard Worker
61*2b949d04SAndroid Build Coastguard Worker6.  Assign values to each vertex such that, for each edge, you can add
62*2b949d04SAndroid Build Coastguard Worker    the values for the two vertices and get the desired (hash) value
63*2b949d04SAndroid Build Coastguard Worker    for that edge.  This task is easy, because the graph is acyclic.
64*2b949d04SAndroid Build Coastguard Worker    This is done by picking a vertex, and assigning it a value of 0.
65*2b949d04SAndroid Build Coastguard Worker    Then do a depth-first search, assigning values to new vertices so that
66*2b949d04SAndroid Build Coastguard Worker    they sum up properly.
67*2b949d04SAndroid Build Coastguard Worker
68*2b949d04SAndroid Build Coastguard Worker7.  f1, f2, and vertex values of G now make up a perfect hash function.
69*2b949d04SAndroid Build Coastguard Worker
70*2b949d04SAndroid Build Coastguard Worker
71*2b949d04SAndroid Build Coastguard WorkerFor simplicity, the implementation of the algorithm combines steps 5 and 6.
72*2b949d04SAndroid Build Coastguard WorkerThat is, we check for loops in G and assign the vertex values in one procedure.
73*2b949d04SAndroid Build Coastguard WorkerIf this procedure succeeds, G is acyclic and the vertex values are assigned.
74*2b949d04SAndroid Build Coastguard WorkerIf the procedure fails, G is cyclic, and we go back to step 2, replacing G
75*2b949d04SAndroid Build Coastguard Workerwith a new graph, and thereby discarding the vertex values from the failed
76*2b949d04SAndroid Build Coastguard Workerattempt.
77*2b949d04SAndroid Build Coastguard Worker"""
78*2b949d04SAndroid Build Coastguard Workerfrom __future__ import absolute_import, division, print_function
79*2b949d04SAndroid Build Coastguard Worker
80*2b949d04SAndroid Build Coastguard Workerimport sys
81*2b949d04SAndroid Build Coastguard Workerimport random
82*2b949d04SAndroid Build Coastguard Workerimport string
83*2b949d04SAndroid Build Coastguard Workerimport subprocess
84*2b949d04SAndroid Build Coastguard Workerimport shutil
85*2b949d04SAndroid Build Coastguard Workerimport tempfile
86*2b949d04SAndroid Build Coastguard Workerfrom collections import defaultdict
87*2b949d04SAndroid Build Coastguard Workerfrom os.path import join
88*2b949d04SAndroid Build Coastguard Worker
89*2b949d04SAndroid Build Coastguard Workerif sys.version_info[0] == 2:
90*2b949d04SAndroid Build Coastguard Worker    from cStringIO import StringIO
91*2b949d04SAndroid Build Coastguard Workerelse:
92*2b949d04SAndroid Build Coastguard Worker    from io import StringIO
93*2b949d04SAndroid Build Coastguard Worker
94*2b949d04SAndroid Build Coastguard Worker
95*2b949d04SAndroid Build Coastguard Worker__version__ = '0.4.2'
96*2b949d04SAndroid Build Coastguard Worker
97*2b949d04SAndroid Build Coastguard Worker
98*2b949d04SAndroid Build Coastguard Workerverbose = False
99*2b949d04SAndroid Build Coastguard Workertrials = 150
100*2b949d04SAndroid Build Coastguard Worker
101*2b949d04SAndroid Build Coastguard Worker
102*2b949d04SAndroid Build Coastguard Workerclass Graph(object):
103*2b949d04SAndroid Build Coastguard Worker    """
104*2b949d04SAndroid Build Coastguard Worker    Implements a graph with 'N' vertices.  First, you connect the graph with
105*2b949d04SAndroid Build Coastguard Worker    edges, which have a desired value associated.  Then the vertex values
106*2b949d04SAndroid Build Coastguard Worker    are assigned, which will fail if the graph is cyclic.  The vertex values
107*2b949d04SAndroid Build Coastguard Worker    are assigned such that the two values corresponding to an edge add up to
108*2b949d04SAndroid Build Coastguard Worker    the desired edge value (mod N).
109*2b949d04SAndroid Build Coastguard Worker    """
110*2b949d04SAndroid Build Coastguard Worker    def __init__(self, N):
111*2b949d04SAndroid Build Coastguard Worker        self.N = N                     # number of vertices
112*2b949d04SAndroid Build Coastguard Worker
113*2b949d04SAndroid Build Coastguard Worker        # maps a vertex number to the list of tuples (vertex, edge value)
114*2b949d04SAndroid Build Coastguard Worker        # to which it is connected by edges.
115*2b949d04SAndroid Build Coastguard Worker        self.adjacent = defaultdict(list)
116*2b949d04SAndroid Build Coastguard Worker
117*2b949d04SAndroid Build Coastguard Worker    def connect(self, vertex1, vertex2, edge_value):
118*2b949d04SAndroid Build Coastguard Worker        """
119*2b949d04SAndroid Build Coastguard Worker        Connect 'vertex1' and 'vertex2' with an edge, with associated
120*2b949d04SAndroid Build Coastguard Worker        value 'value'
121*2b949d04SAndroid Build Coastguard Worker        """
122*2b949d04SAndroid Build Coastguard Worker        # Add vertices to each other's adjacent list
123*2b949d04SAndroid Build Coastguard Worker        self.adjacent[vertex1].append((vertex2, edge_value))
124*2b949d04SAndroid Build Coastguard Worker        self.adjacent[vertex2].append((vertex1, edge_value))
125*2b949d04SAndroid Build Coastguard Worker
126*2b949d04SAndroid Build Coastguard Worker    def assign_vertex_values(self):
127*2b949d04SAndroid Build Coastguard Worker        """
128*2b949d04SAndroid Build Coastguard Worker        Try to assign the vertex values, such that, for each edge, you can
129*2b949d04SAndroid Build Coastguard Worker        add the values for the two vertices involved and get the desired
130*2b949d04SAndroid Build Coastguard Worker        value for that edge, i.e. the desired hash key.
131*2b949d04SAndroid Build Coastguard Worker        This will fail when the graph is cyclic.
132*2b949d04SAndroid Build Coastguard Worker
133*2b949d04SAndroid Build Coastguard Worker        This is done by a Depth-First Search of the graph.  If the search
134*2b949d04SAndroid Build Coastguard Worker        finds a vertex that was visited before, there's a loop and False is
135*2b949d04SAndroid Build Coastguard Worker        returned immediately, i.e. the assignment is terminated.
136*2b949d04SAndroid Build Coastguard Worker        On success (when the graph is acyclic) True is returned.
137*2b949d04SAndroid Build Coastguard Worker        """
138*2b949d04SAndroid Build Coastguard Worker        self.vertex_values = self.N * [-1]  # -1 means unassigned
139*2b949d04SAndroid Build Coastguard Worker
140*2b949d04SAndroid Build Coastguard Worker        visited = self.N * [False]
141*2b949d04SAndroid Build Coastguard Worker
142*2b949d04SAndroid Build Coastguard Worker        # Loop over all vertices, taking unvisited ones as roots.
143*2b949d04SAndroid Build Coastguard Worker        for root in range(self.N):
144*2b949d04SAndroid Build Coastguard Worker            if visited[root]:
145*2b949d04SAndroid Build Coastguard Worker                continue
146*2b949d04SAndroid Build Coastguard Worker
147*2b949d04SAndroid Build Coastguard Worker            # explore tree starting at 'root'
148*2b949d04SAndroid Build Coastguard Worker            self.vertex_values[root] = 0    # set arbitrarily to zero
149*2b949d04SAndroid Build Coastguard Worker
150*2b949d04SAndroid Build Coastguard Worker            # Stack of vertices to visit, a list of tuples (parent, vertex)
151*2b949d04SAndroid Build Coastguard Worker            tovisit = [(None, root)]
152*2b949d04SAndroid Build Coastguard Worker            while tovisit:
153*2b949d04SAndroid Build Coastguard Worker                parent, vertex = tovisit.pop()
154*2b949d04SAndroid Build Coastguard Worker                visited[vertex] = True
155*2b949d04SAndroid Build Coastguard Worker
156*2b949d04SAndroid Build Coastguard Worker                # Loop over adjacent vertices, but skip the vertex we arrived
157*2b949d04SAndroid Build Coastguard Worker                # here from the first time it is encountered.
158*2b949d04SAndroid Build Coastguard Worker                skip = True
159*2b949d04SAndroid Build Coastguard Worker                for neighbor, edge_value in self.adjacent[vertex]:
160*2b949d04SAndroid Build Coastguard Worker                    if skip and neighbor == parent:
161*2b949d04SAndroid Build Coastguard Worker                        skip = False
162*2b949d04SAndroid Build Coastguard Worker                        continue
163*2b949d04SAndroid Build Coastguard Worker
164*2b949d04SAndroid Build Coastguard Worker                    if visited[neighbor]:
165*2b949d04SAndroid Build Coastguard Worker                        # We visited here before, so the graph is cyclic.
166*2b949d04SAndroid Build Coastguard Worker                        return False
167*2b949d04SAndroid Build Coastguard Worker
168*2b949d04SAndroid Build Coastguard Worker                    tovisit.append((vertex, neighbor))
169*2b949d04SAndroid Build Coastguard Worker
170*2b949d04SAndroid Build Coastguard Worker                    # Set new vertex's value to the desired edge value,
171*2b949d04SAndroid Build Coastguard Worker                    # minus the value of the vertex we came here from.
172*2b949d04SAndroid Build Coastguard Worker                    self.vertex_values[neighbor] = (
173*2b949d04SAndroid Build Coastguard Worker                        edge_value - self.vertex_values[vertex]) % self.N
174*2b949d04SAndroid Build Coastguard Worker
175*2b949d04SAndroid Build Coastguard Worker        # check if all vertices have a valid value
176*2b949d04SAndroid Build Coastguard Worker        for vertex in range(self.N):
177*2b949d04SAndroid Build Coastguard Worker            assert self.vertex_values[vertex] >= 0
178*2b949d04SAndroid Build Coastguard Worker
179*2b949d04SAndroid Build Coastguard Worker        # We got though, so the graph is acyclic,
180*2b949d04SAndroid Build Coastguard Worker        # and all values are now assigned.
181*2b949d04SAndroid Build Coastguard Worker        return True
182*2b949d04SAndroid Build Coastguard Worker
183*2b949d04SAndroid Build Coastguard Worker
184*2b949d04SAndroid Build Coastguard Workerclass StrSaltHash(object):
185*2b949d04SAndroid Build Coastguard Worker    """
186*2b949d04SAndroid Build Coastguard Worker    Random hash function generator.
187*2b949d04SAndroid Build Coastguard Worker    Simple byte level hashing: each byte is multiplied to another byte from
188*2b949d04SAndroid Build Coastguard Worker    a random string of characters, summed up, and finally modulo NG is
189*2b949d04SAndroid Build Coastguard Worker    taken.
190*2b949d04SAndroid Build Coastguard Worker    """
191*2b949d04SAndroid Build Coastguard Worker    chars = string.ascii_letters + string.digits
192*2b949d04SAndroid Build Coastguard Worker
193*2b949d04SAndroid Build Coastguard Worker    def __init__(self, N):
194*2b949d04SAndroid Build Coastguard Worker        self.N = N
195*2b949d04SAndroid Build Coastguard Worker        self.salt = ''
196*2b949d04SAndroid Build Coastguard Worker
197*2b949d04SAndroid Build Coastguard Worker    def __call__(self, key):
198*2b949d04SAndroid Build Coastguard Worker        # XXX: xkbcommon modification: make the salt length a power of 2
199*2b949d04SAndroid Build Coastguard Worker        #      so that the % operation in the hash is fast.
200*2b949d04SAndroid Build Coastguard Worker        while len(self.salt) < max(len(key), 32): # add more salt as necessary
201*2b949d04SAndroid Build Coastguard Worker            self.salt += random.choice(self.chars)
202*2b949d04SAndroid Build Coastguard Worker
203*2b949d04SAndroid Build Coastguard Worker        return sum(ord(self.salt[i]) * ord(c)
204*2b949d04SAndroid Build Coastguard Worker                   for i, c in enumerate(key)) % self.N
205*2b949d04SAndroid Build Coastguard Worker
206*2b949d04SAndroid Build Coastguard Worker    template = """
207*2b949d04SAndroid Build Coastguard Workerdef hash_f(key, T):
208*2b949d04SAndroid Build Coastguard Worker    return sum(ord(T[i % $NS]) * ord(c) for i, c in enumerate(key)) % $NG
209*2b949d04SAndroid Build Coastguard Worker
210*2b949d04SAndroid Build Coastguard Workerdef perfect_hash(key):
211*2b949d04SAndroid Build Coastguard Worker    return (G[hash_f(key, "$S1")] +
212*2b949d04SAndroid Build Coastguard Worker            G[hash_f(key, "$S2")]) % $NG
213*2b949d04SAndroid Build Coastguard Worker"""
214*2b949d04SAndroid Build Coastguard Worker
215*2b949d04SAndroid Build Coastguard Workerclass IntSaltHash(object):
216*2b949d04SAndroid Build Coastguard Worker    """
217*2b949d04SAndroid Build Coastguard Worker    Random hash function generator.
218*2b949d04SAndroid Build Coastguard Worker    Simple byte level hashing, each byte is multiplied in sequence to a table
219*2b949d04SAndroid Build Coastguard Worker    containing random numbers, summed tp, and finally modulo NG is taken.
220*2b949d04SAndroid Build Coastguard Worker    """
221*2b949d04SAndroid Build Coastguard Worker    def __init__(self, N):
222*2b949d04SAndroid Build Coastguard Worker        self.N = N
223*2b949d04SAndroid Build Coastguard Worker        self.salt = []
224*2b949d04SAndroid Build Coastguard Worker
225*2b949d04SAndroid Build Coastguard Worker    def __call__(self, key):
226*2b949d04SAndroid Build Coastguard Worker        while len(self.salt) < len(key): # add more salt as necessary
227*2b949d04SAndroid Build Coastguard Worker            self.salt.append(random.randint(1, self.N - 1))
228*2b949d04SAndroid Build Coastguard Worker
229*2b949d04SAndroid Build Coastguard Worker        return sum(self.salt[i] * ord(c)
230*2b949d04SAndroid Build Coastguard Worker                   for i, c in enumerate(key)) % self.N
231*2b949d04SAndroid Build Coastguard Worker
232*2b949d04SAndroid Build Coastguard Worker    template = """
233*2b949d04SAndroid Build Coastguard WorkerS1 = [$S1]
234*2b949d04SAndroid Build Coastguard WorkerS2 = [$S2]
235*2b949d04SAndroid Build Coastguard Workerassert len(S1) == len(S2) == $NS
236*2b949d04SAndroid Build Coastguard Worker
237*2b949d04SAndroid Build Coastguard Workerdef hash_f(key, T):
238*2b949d04SAndroid Build Coastguard Worker    return sum(T[i % $NS] * ord(c) for i, c in enumerate(key)) % $NG
239*2b949d04SAndroid Build Coastguard Worker
240*2b949d04SAndroid Build Coastguard Workerdef perfect_hash(key):
241*2b949d04SAndroid Build Coastguard Worker    return (G[hash_f(key, S1)] + G[hash_f(key, S2)]) % $NG
242*2b949d04SAndroid Build Coastguard Worker"""
243*2b949d04SAndroid Build Coastguard Worker
244*2b949d04SAndroid Build Coastguard Workerdef builtin_template(Hash):
245*2b949d04SAndroid Build Coastguard Worker    return """\
246*2b949d04SAndroid Build Coastguard Worker# =======================================================================
247*2b949d04SAndroid Build Coastguard Worker# ================= Python code for perfect hash function ===============
248*2b949d04SAndroid Build Coastguard Worker# =======================================================================
249*2b949d04SAndroid Build Coastguard Worker
250*2b949d04SAndroid Build Coastguard WorkerG = [$G]
251*2b949d04SAndroid Build Coastguard Worker""" + Hash.template + """
252*2b949d04SAndroid Build Coastguard Worker# ============================ Sanity check =============================
253*2b949d04SAndroid Build Coastguard Worker
254*2b949d04SAndroid Build Coastguard WorkerK = [$K]
255*2b949d04SAndroid Build Coastguard Workerassert len(K) == $NK
256*2b949d04SAndroid Build Coastguard Worker
257*2b949d04SAndroid Build Coastguard Workerfor h, k in enumerate(K):
258*2b949d04SAndroid Build Coastguard Worker    assert perfect_hash(k) == h
259*2b949d04SAndroid Build Coastguard Worker"""
260*2b949d04SAndroid Build Coastguard Worker
261*2b949d04SAndroid Build Coastguard Worker
262*2b949d04SAndroid Build Coastguard Workerclass TooManyInterationsError(Exception):
263*2b949d04SAndroid Build Coastguard Worker    pass
264*2b949d04SAndroid Build Coastguard Worker
265*2b949d04SAndroid Build Coastguard Worker
266*2b949d04SAndroid Build Coastguard Workerdef generate_hash(keys, Hash=StrSaltHash):
267*2b949d04SAndroid Build Coastguard Worker    """
268*2b949d04SAndroid Build Coastguard Worker    Return hash functions f1 and f2, and G for a perfect minimal hash.
269*2b949d04SAndroid Build Coastguard Worker    Input is an iterable of 'keys', whos indicies are the desired hash values.
270*2b949d04SAndroid Build Coastguard Worker    'Hash' is a random hash function generator, that means Hash(N) returns a
271*2b949d04SAndroid Build Coastguard Worker    returns a random hash function which returns hash values from 0..N-1.
272*2b949d04SAndroid Build Coastguard Worker    """
273*2b949d04SAndroid Build Coastguard Worker    if not isinstance(keys, (list, tuple)):
274*2b949d04SAndroid Build Coastguard Worker        raise TypeError("list or tuple expected")
275*2b949d04SAndroid Build Coastguard Worker    NK = len(keys)
276*2b949d04SAndroid Build Coastguard Worker    if NK != len(set(keys)):
277*2b949d04SAndroid Build Coastguard Worker        raise ValueError("duplicate keys")
278*2b949d04SAndroid Build Coastguard Worker    for key in keys:
279*2b949d04SAndroid Build Coastguard Worker        if not isinstance(key, str):
280*2b949d04SAndroid Build Coastguard Worker            raise TypeError("key a not string: %r" % key)
281*2b949d04SAndroid Build Coastguard Worker    if NK > 10000 and Hash == StrSaltHash:
282*2b949d04SAndroid Build Coastguard Worker        print("""\
283*2b949d04SAndroid Build Coastguard WorkerWARNING: You have %d keys.
284*2b949d04SAndroid Build Coastguard Worker         Using --hft=1 is likely to fail for so many keys.
285*2b949d04SAndroid Build Coastguard Worker         Please use --hft=2 instead.
286*2b949d04SAndroid Build Coastguard Worker""" % NK)
287*2b949d04SAndroid Build Coastguard Worker
288*2b949d04SAndroid Build Coastguard Worker    # the number of vertices in the graph G
289*2b949d04SAndroid Build Coastguard Worker    NG = NK + 1
290*2b949d04SAndroid Build Coastguard Worker    if verbose:
291*2b949d04SAndroid Build Coastguard Worker        print('NG = %d' % NG)
292*2b949d04SAndroid Build Coastguard Worker
293*2b949d04SAndroid Build Coastguard Worker    trial = 0  # Number of trial graphs so far
294*2b949d04SAndroid Build Coastguard Worker    while True:
295*2b949d04SAndroid Build Coastguard Worker        if (trial % trials) == 0:   # trials failures, increase NG slightly
296*2b949d04SAndroid Build Coastguard Worker            if trial > 0:
297*2b949d04SAndroid Build Coastguard Worker                NG = max(NG + 1, int(1.05 * NG))
298*2b949d04SAndroid Build Coastguard Worker            if verbose:
299*2b949d04SAndroid Build Coastguard Worker                sys.stdout.write('\nGenerating graphs NG = %d ' % NG)
300*2b949d04SAndroid Build Coastguard Worker        trial += 1
301*2b949d04SAndroid Build Coastguard Worker
302*2b949d04SAndroid Build Coastguard Worker        if NG > 100 * (NK + 1):
303*2b949d04SAndroid Build Coastguard Worker            raise TooManyInterationsError("%d keys" % NK)
304*2b949d04SAndroid Build Coastguard Worker
305*2b949d04SAndroid Build Coastguard Worker        if verbose:
306*2b949d04SAndroid Build Coastguard Worker            sys.stdout.write('.')
307*2b949d04SAndroid Build Coastguard Worker            sys.stdout.flush()
308*2b949d04SAndroid Build Coastguard Worker
309*2b949d04SAndroid Build Coastguard Worker        G = Graph(NG)   # Create graph with NG vertices
310*2b949d04SAndroid Build Coastguard Worker        f1 = Hash(NG)   # Create 2 random hash functions
311*2b949d04SAndroid Build Coastguard Worker        f2 = Hash(NG)
312*2b949d04SAndroid Build Coastguard Worker
313*2b949d04SAndroid Build Coastguard Worker        # Connect vertices given by the values of the two hash functions
314*2b949d04SAndroid Build Coastguard Worker        # for each key.  Associate the desired hash value with each edge.
315*2b949d04SAndroid Build Coastguard Worker        for hashval, key in enumerate(keys):
316*2b949d04SAndroid Build Coastguard Worker            G.connect(f1(key), f2(key), hashval)
317*2b949d04SAndroid Build Coastguard Worker
318*2b949d04SAndroid Build Coastguard Worker        # Try to assign the vertex values.  This will fail when the graph
319*2b949d04SAndroid Build Coastguard Worker        # is cyclic.  But when the graph is acyclic it will succeed and we
320*2b949d04SAndroid Build Coastguard Worker        # break out, because we're done.
321*2b949d04SAndroid Build Coastguard Worker        if G.assign_vertex_values():
322*2b949d04SAndroid Build Coastguard Worker            break
323*2b949d04SAndroid Build Coastguard Worker
324*2b949d04SAndroid Build Coastguard Worker    if verbose:
325*2b949d04SAndroid Build Coastguard Worker        print('\nAcyclic graph found after %d trials.' % trial)
326*2b949d04SAndroid Build Coastguard Worker        print('NG = %d' % NG)
327*2b949d04SAndroid Build Coastguard Worker
328*2b949d04SAndroid Build Coastguard Worker    # Sanity check the result by actually verifying that all the keys
329*2b949d04SAndroid Build Coastguard Worker    # hash to the right value.
330*2b949d04SAndroid Build Coastguard Worker    for hashval, key in enumerate(keys):
331*2b949d04SAndroid Build Coastguard Worker        assert hashval == (
332*2b949d04SAndroid Build Coastguard Worker            G.vertex_values[f1(key)] + G.vertex_values[f2(key)]
333*2b949d04SAndroid Build Coastguard Worker        ) % NG
334*2b949d04SAndroid Build Coastguard Worker
335*2b949d04SAndroid Build Coastguard Worker    if verbose:
336*2b949d04SAndroid Build Coastguard Worker        print('OK')
337*2b949d04SAndroid Build Coastguard Worker
338*2b949d04SAndroid Build Coastguard Worker    return f1, f2, G.vertex_values
339*2b949d04SAndroid Build Coastguard Worker
340*2b949d04SAndroid Build Coastguard Worker
341*2b949d04SAndroid Build Coastguard Workerclass Format(object):
342*2b949d04SAndroid Build Coastguard Worker
343*2b949d04SAndroid Build Coastguard Worker    def __init__(self, width=76, indent=4, delimiter=', '):
344*2b949d04SAndroid Build Coastguard Worker        self.width = width
345*2b949d04SAndroid Build Coastguard Worker        self.indent = indent
346*2b949d04SAndroid Build Coastguard Worker        self.delimiter = delimiter
347*2b949d04SAndroid Build Coastguard Worker
348*2b949d04SAndroid Build Coastguard Worker    def print_format(self):
349*2b949d04SAndroid Build Coastguard Worker        print("Format options:")
350*2b949d04SAndroid Build Coastguard Worker        for name in 'width', 'indent', 'delimiter':
351*2b949d04SAndroid Build Coastguard Worker            print('  %s: %r' % (name, getattr(self, name)))
352*2b949d04SAndroid Build Coastguard Worker
353*2b949d04SAndroid Build Coastguard Worker    def __call__(self, data, quote=False):
354*2b949d04SAndroid Build Coastguard Worker        if not isinstance(data, (list, tuple)):
355*2b949d04SAndroid Build Coastguard Worker            return str(data)
356*2b949d04SAndroid Build Coastguard Worker
357*2b949d04SAndroid Build Coastguard Worker        lendel = len(self.delimiter)
358*2b949d04SAndroid Build Coastguard Worker        aux = StringIO()
359*2b949d04SAndroid Build Coastguard Worker        pos = 20
360*2b949d04SAndroid Build Coastguard Worker        for i, elt in enumerate(data):
361*2b949d04SAndroid Build Coastguard Worker            last = bool(i == len(data) - 1)
362*2b949d04SAndroid Build Coastguard Worker
363*2b949d04SAndroid Build Coastguard Worker            s = ('"%s"' if quote else '%s') % elt
364*2b949d04SAndroid Build Coastguard Worker
365*2b949d04SAndroid Build Coastguard Worker            if pos + len(s) + lendel > self.width:
366*2b949d04SAndroid Build Coastguard Worker                aux.write('\n' + (self.indent * ' '))
367*2b949d04SAndroid Build Coastguard Worker                pos = self.indent
368*2b949d04SAndroid Build Coastguard Worker
369*2b949d04SAndroid Build Coastguard Worker            aux.write(s)
370*2b949d04SAndroid Build Coastguard Worker            pos += len(s)
371*2b949d04SAndroid Build Coastguard Worker            if not last:
372*2b949d04SAndroid Build Coastguard Worker                aux.write(self.delimiter)
373*2b949d04SAndroid Build Coastguard Worker                pos += lendel
374*2b949d04SAndroid Build Coastguard Worker
375*2b949d04SAndroid Build Coastguard Worker        return '\n'.join(l.rstrip() for l in aux.getvalue().split('\n'))
376*2b949d04SAndroid Build Coastguard Worker
377*2b949d04SAndroid Build Coastguard Worker
378*2b949d04SAndroid Build Coastguard Workerdef generate_code(keys, Hash=StrSaltHash, template=None, options=None):
379*2b949d04SAndroid Build Coastguard Worker    """
380*2b949d04SAndroid Build Coastguard Worker    Takes a list of key value pairs and inserts the generated parameter
381*2b949d04SAndroid Build Coastguard Worker    lists into the 'template' string.  'Hash' is the random hash function
382*2b949d04SAndroid Build Coastguard Worker    generator, and the optional keywords are formating options.
383*2b949d04SAndroid Build Coastguard Worker    The return value is the substituted code template.
384*2b949d04SAndroid Build Coastguard Worker    """
385*2b949d04SAndroid Build Coastguard Worker    f1, f2, G = generate_hash(keys, Hash)
386*2b949d04SAndroid Build Coastguard Worker
387*2b949d04SAndroid Build Coastguard Worker    assert f1.N == f2.N == len(G)
388*2b949d04SAndroid Build Coastguard Worker    try:
389*2b949d04SAndroid Build Coastguard Worker        salt_len = len(f1.salt)
390*2b949d04SAndroid Build Coastguard Worker        assert salt_len == len(f2.salt)
391*2b949d04SAndroid Build Coastguard Worker    except TypeError:
392*2b949d04SAndroid Build Coastguard Worker        salt_len = None
393*2b949d04SAndroid Build Coastguard Worker
394*2b949d04SAndroid Build Coastguard Worker    if template is None:
395*2b949d04SAndroid Build Coastguard Worker        template = builtin_template(Hash)
396*2b949d04SAndroid Build Coastguard Worker
397*2b949d04SAndroid Build Coastguard Worker    if options is None:
398*2b949d04SAndroid Build Coastguard Worker        fmt = Format()
399*2b949d04SAndroid Build Coastguard Worker    else:
400*2b949d04SAndroid Build Coastguard Worker        fmt = Format(width=options.width, indent=options.indent,
401*2b949d04SAndroid Build Coastguard Worker                     delimiter=options.delimiter)
402*2b949d04SAndroid Build Coastguard Worker
403*2b949d04SAndroid Build Coastguard Worker    if verbose:
404*2b949d04SAndroid Build Coastguard Worker        fmt.print_format()
405*2b949d04SAndroid Build Coastguard Worker
406*2b949d04SAndroid Build Coastguard Worker    return string.Template(template).substitute(
407*2b949d04SAndroid Build Coastguard Worker        NS = salt_len,
408*2b949d04SAndroid Build Coastguard Worker        S1 = fmt(f1.salt),
409*2b949d04SAndroid Build Coastguard Worker        S2 = fmt(f2.salt),
410*2b949d04SAndroid Build Coastguard Worker        NG = len(G),
411*2b949d04SAndroid Build Coastguard Worker        G  = fmt(G),
412*2b949d04SAndroid Build Coastguard Worker        NK = len(keys),
413*2b949d04SAndroid Build Coastguard Worker        K  = fmt(list(keys), quote=True))
414*2b949d04SAndroid Build Coastguard Worker
415*2b949d04SAndroid Build Coastguard Worker
416*2b949d04SAndroid Build Coastguard Workerdef read_table(filename, options):
417*2b949d04SAndroid Build Coastguard Worker    """
418*2b949d04SAndroid Build Coastguard Worker    Reads keys and desired hash value pairs from a file.  If no column
419*2b949d04SAndroid Build Coastguard Worker    for the hash value is specified, a sequence of hash values is generated,
420*2b949d04SAndroid Build Coastguard Worker    from 0 to N-1, where N is the number of rows found in the file.
421*2b949d04SAndroid Build Coastguard Worker    """
422*2b949d04SAndroid Build Coastguard Worker    if verbose:
423*2b949d04SAndroid Build Coastguard Worker        print("Reading table from file `%s' to extract keys." % filename)
424*2b949d04SAndroid Build Coastguard Worker    try:
425*2b949d04SAndroid Build Coastguard Worker        fi = open(filename)
426*2b949d04SAndroid Build Coastguard Worker    except IOError:
427*2b949d04SAndroid Build Coastguard Worker        sys.exit("Error: Could not open `%s' for reading." % filename)
428*2b949d04SAndroid Build Coastguard Worker
429*2b949d04SAndroid Build Coastguard Worker    keys = []
430*2b949d04SAndroid Build Coastguard Worker
431*2b949d04SAndroid Build Coastguard Worker    if verbose:
432*2b949d04SAndroid Build Coastguard Worker        print("Reader options:")
433*2b949d04SAndroid Build Coastguard Worker        for name in 'comment', 'splitby', 'keycol':
434*2b949d04SAndroid Build Coastguard Worker            print('  %s: %r' % (name, getattr(options, name)))
435*2b949d04SAndroid Build Coastguard Worker
436*2b949d04SAndroid Build Coastguard Worker    for n, line in enumerate(fi):
437*2b949d04SAndroid Build Coastguard Worker        line = line.strip()
438*2b949d04SAndroid Build Coastguard Worker        if not line or line.startswith(options.comment):
439*2b949d04SAndroid Build Coastguard Worker            continue
440*2b949d04SAndroid Build Coastguard Worker
441*2b949d04SAndroid Build Coastguard Worker        if line.count(options.comment): # strip content after comment
442*2b949d04SAndroid Build Coastguard Worker            line = line.split(options.comment)[0].strip()
443*2b949d04SAndroid Build Coastguard Worker
444*2b949d04SAndroid Build Coastguard Worker        row = [col.strip() for col in line.split(options.splitby)]
445*2b949d04SAndroid Build Coastguard Worker
446*2b949d04SAndroid Build Coastguard Worker        try:
447*2b949d04SAndroid Build Coastguard Worker            key = row[options.keycol - 1]
448*2b949d04SAndroid Build Coastguard Worker        except IndexError:
449*2b949d04SAndroid Build Coastguard Worker            sys.exit("%s:%d: Error: Cannot read key, not enough columns." %
450*2b949d04SAndroid Build Coastguard Worker                     (filename, n + 1))
451*2b949d04SAndroid Build Coastguard Worker
452*2b949d04SAndroid Build Coastguard Worker        keys.append(key)
453*2b949d04SAndroid Build Coastguard Worker
454*2b949d04SAndroid Build Coastguard Worker    fi.close()
455*2b949d04SAndroid Build Coastguard Worker
456*2b949d04SAndroid Build Coastguard Worker    if not keys:
457*2b949d04SAndroid Build Coastguard Worker        exit("Error: no keys found in file `%s'." % filename)
458*2b949d04SAndroid Build Coastguard Worker
459*2b949d04SAndroid Build Coastguard Worker    return keys
460*2b949d04SAndroid Build Coastguard Worker
461*2b949d04SAndroid Build Coastguard Worker
462*2b949d04SAndroid Build Coastguard Workerdef read_template(filename):
463*2b949d04SAndroid Build Coastguard Worker    if verbose:
464*2b949d04SAndroid Build Coastguard Worker        print("Reading template from file `%s'" % filename)
465*2b949d04SAndroid Build Coastguard Worker    try:
466*2b949d04SAndroid Build Coastguard Worker        with open(filename, 'r') as fi:
467*2b949d04SAndroid Build Coastguard Worker            return fi.read()
468*2b949d04SAndroid Build Coastguard Worker    except IOError:
469*2b949d04SAndroid Build Coastguard Worker        sys.exit("Error: Could not open `%s' for reading." % filename)
470*2b949d04SAndroid Build Coastguard Worker
471*2b949d04SAndroid Build Coastguard Worker
472*2b949d04SAndroid Build Coastguard Workerdef run_code(code):
473*2b949d04SAndroid Build Coastguard Worker    tmpdir = tempfile.mkdtemp()
474*2b949d04SAndroid Build Coastguard Worker    path = join(tmpdir, 't.py')
475*2b949d04SAndroid Build Coastguard Worker    with open(path, 'w') as fo:
476*2b949d04SAndroid Build Coastguard Worker        fo.write(code)
477*2b949d04SAndroid Build Coastguard Worker    try:
478*2b949d04SAndroid Build Coastguard Worker        subprocess.check_call([sys.executable, path])
479*2b949d04SAndroid Build Coastguard Worker    except subprocess.CalledProcessError as e:
480*2b949d04SAndroid Build Coastguard Worker        raise AssertionError(e)
481*2b949d04SAndroid Build Coastguard Worker    finally:
482*2b949d04SAndroid Build Coastguard Worker        shutil.rmtree(tmpdir)
483*2b949d04SAndroid Build Coastguard Worker
484*2b949d04SAndroid Build Coastguard Worker
485*2b949d04SAndroid Build Coastguard Workerdef main():
486*2b949d04SAndroid Build Coastguard Worker    from optparse import OptionParser
487*2b949d04SAndroid Build Coastguard Worker
488*2b949d04SAndroid Build Coastguard Worker    usage = "usage: %prog [options] KEYS_FILE [TMPL_FILE]"
489*2b949d04SAndroid Build Coastguard Worker
490*2b949d04SAndroid Build Coastguard Worker    description = """\
491*2b949d04SAndroid Build Coastguard WorkerGenerates code for perfect hash functions from
492*2b949d04SAndroid Build Coastguard Workera file with keywords and a code template.
493*2b949d04SAndroid Build Coastguard WorkerIf no template file is provided, a small built-in Python template
494*2b949d04SAndroid Build Coastguard Workeris processed and the output code is written to stdout.
495*2b949d04SAndroid Build Coastguard Worker"""
496*2b949d04SAndroid Build Coastguard Worker
497*2b949d04SAndroid Build Coastguard Worker    parser = OptionParser(usage = usage,
498*2b949d04SAndroid Build Coastguard Worker                          description = description,
499*2b949d04SAndroid Build Coastguard Worker                          prog = sys.argv[0],
500*2b949d04SAndroid Build Coastguard Worker                          version = "%prog: " + __version__)
501*2b949d04SAndroid Build Coastguard Worker
502*2b949d04SAndroid Build Coastguard Worker    parser.add_option("--delimiter",
503*2b949d04SAndroid Build Coastguard Worker                      action  = "store",
504*2b949d04SAndroid Build Coastguard Worker                      default = ", ",
505*2b949d04SAndroid Build Coastguard Worker                      help    = "Delimiter for list items used in output, "
506*2b949d04SAndroid Build Coastguard Worker                                "the default delimiter is '%default'",
507*2b949d04SAndroid Build Coastguard Worker                      metavar = "STR")
508*2b949d04SAndroid Build Coastguard Worker
509*2b949d04SAndroid Build Coastguard Worker    parser.add_option("--indent",
510*2b949d04SAndroid Build Coastguard Worker                      action  = "store",
511*2b949d04SAndroid Build Coastguard Worker                      default = 4,
512*2b949d04SAndroid Build Coastguard Worker                      type    = "int",
513*2b949d04SAndroid Build Coastguard Worker                      help    = "Make INT spaces at the beginning of a "
514*2b949d04SAndroid Build Coastguard Worker                                "new line when generated list is wrapped. "
515*2b949d04SAndroid Build Coastguard Worker                                "Default is %default",
516*2b949d04SAndroid Build Coastguard Worker                      metavar = "INT")
517*2b949d04SAndroid Build Coastguard Worker
518*2b949d04SAndroid Build Coastguard Worker    parser.add_option("--width",
519*2b949d04SAndroid Build Coastguard Worker                      action  = "store",
520*2b949d04SAndroid Build Coastguard Worker                      default = 76,
521*2b949d04SAndroid Build Coastguard Worker                      type    = "int",
522*2b949d04SAndroid Build Coastguard Worker                      help    = "Maximal width of generated list when "
523*2b949d04SAndroid Build Coastguard Worker                                "wrapped.  Default width is %default",
524*2b949d04SAndroid Build Coastguard Worker                      metavar = "INT")
525*2b949d04SAndroid Build Coastguard Worker
526*2b949d04SAndroid Build Coastguard Worker    parser.add_option("--comment",
527*2b949d04SAndroid Build Coastguard Worker                      action  = "store",
528*2b949d04SAndroid Build Coastguard Worker                      default = "#",
529*2b949d04SAndroid Build Coastguard Worker                      help    = "STR is the character, or sequence of "
530*2b949d04SAndroid Build Coastguard Worker                                "characters, which marks the beginning "
531*2b949d04SAndroid Build Coastguard Worker                                "of a comment (which runs till "
532*2b949d04SAndroid Build Coastguard Worker                                "the end of the line), in the input "
533*2b949d04SAndroid Build Coastguard Worker                                "KEYS_FILE. "
534*2b949d04SAndroid Build Coastguard Worker                                "Default is '%default'",
535*2b949d04SAndroid Build Coastguard Worker                      metavar = "STR")
536*2b949d04SAndroid Build Coastguard Worker
537*2b949d04SAndroid Build Coastguard Worker    parser.add_option("--splitby",
538*2b949d04SAndroid Build Coastguard Worker                      action  = "store",
539*2b949d04SAndroid Build Coastguard Worker                      default = ",",
540*2b949d04SAndroid Build Coastguard Worker                      help    = "STR is the character by which the columns "
541*2b949d04SAndroid Build Coastguard Worker                                "in the input KEYS_FILE are split. "
542*2b949d04SAndroid Build Coastguard Worker                                "Default is '%default'",
543*2b949d04SAndroid Build Coastguard Worker                      metavar = "STR")
544*2b949d04SAndroid Build Coastguard Worker
545*2b949d04SAndroid Build Coastguard Worker    parser.add_option("--keycol",
546*2b949d04SAndroid Build Coastguard Worker                      action  = "store",
547*2b949d04SAndroid Build Coastguard Worker                      default = 1,
548*2b949d04SAndroid Build Coastguard Worker                      type    = "int",
549*2b949d04SAndroid Build Coastguard Worker                      help    = "Specifies the column INT in the input "
550*2b949d04SAndroid Build Coastguard Worker                                "KEYS_FILE which contains the keys. "
551*2b949d04SAndroid Build Coastguard Worker                                "Default is %default, i.e. the first column.",
552*2b949d04SAndroid Build Coastguard Worker                      metavar = "INT")
553*2b949d04SAndroid Build Coastguard Worker
554*2b949d04SAndroid Build Coastguard Worker    parser.add_option("--trials",
555*2b949d04SAndroid Build Coastguard Worker                      action  = "store",
556*2b949d04SAndroid Build Coastguard Worker                      default = 5,
557*2b949d04SAndroid Build Coastguard Worker                      type    = "int",
558*2b949d04SAndroid Build Coastguard Worker                      help    = "Specifies the number of trials before "
559*2b949d04SAndroid Build Coastguard Worker                                "NG is increased.  A small INT will give "
560*2b949d04SAndroid Build Coastguard Worker                                "compute faster, but the array G will be "
561*2b949d04SAndroid Build Coastguard Worker                                "large.  A large INT will take longer to "
562*2b949d04SAndroid Build Coastguard Worker                                "compute but G will be smaller. "
563*2b949d04SAndroid Build Coastguard Worker                                "Default is %default",
564*2b949d04SAndroid Build Coastguard Worker                      metavar = "INT")
565*2b949d04SAndroid Build Coastguard Worker
566*2b949d04SAndroid Build Coastguard Worker    parser.add_option("--hft",
567*2b949d04SAndroid Build Coastguard Worker                      action  = "store",
568*2b949d04SAndroid Build Coastguard Worker                      default = 1,
569*2b949d04SAndroid Build Coastguard Worker                      type    = "int",
570*2b949d04SAndroid Build Coastguard Worker                      help    = "Hash function type INT.  Possible values "
571*2b949d04SAndroid Build Coastguard Worker                                "are 1 (StrSaltHash) and 2 (IntSaltHash). "
572*2b949d04SAndroid Build Coastguard Worker                                "The default is %default",
573*2b949d04SAndroid Build Coastguard Worker                      metavar = "INT")
574*2b949d04SAndroid Build Coastguard Worker
575*2b949d04SAndroid Build Coastguard Worker    parser.add_option("-e", "--execute",
576*2b949d04SAndroid Build Coastguard Worker                      action  = "store_true",
577*2b949d04SAndroid Build Coastguard Worker                      help    = "Execute the generated code within "
578*2b949d04SAndroid Build Coastguard Worker                                "the Python interpreter.")
579*2b949d04SAndroid Build Coastguard Worker
580*2b949d04SAndroid Build Coastguard Worker    parser.add_option("-o", "--output",
581*2b949d04SAndroid Build Coastguard Worker                      action  = "store",
582*2b949d04SAndroid Build Coastguard Worker                      help    = "Specify output FILE explicitly. "
583*2b949d04SAndroid Build Coastguard Worker                                "`-o std' means standard output. "
584*2b949d04SAndroid Build Coastguard Worker                                "`-o no' means no output. "
585*2b949d04SAndroid Build Coastguard Worker                                "By default, the file name is obtained "
586*2b949d04SAndroid Build Coastguard Worker                                "from the name of the template file by "
587*2b949d04SAndroid Build Coastguard Worker                                "substituting `tmpl' to `code'.",
588*2b949d04SAndroid Build Coastguard Worker                      metavar = "FILE")
589*2b949d04SAndroid Build Coastguard Worker
590*2b949d04SAndroid Build Coastguard Worker    parser.add_option("-v", "--verbose",
591*2b949d04SAndroid Build Coastguard Worker                      action = "store_true",
592*2b949d04SAndroid Build Coastguard Worker                      help = "verbosity")
593*2b949d04SAndroid Build Coastguard Worker
594*2b949d04SAndroid Build Coastguard Worker    options, args = parser.parse_args()
595*2b949d04SAndroid Build Coastguard Worker
596*2b949d04SAndroid Build Coastguard Worker    if options.trials <= 0:
597*2b949d04SAndroid Build Coastguard Worker        parser.error("trials before increasing N has to be larger than zero")
598*2b949d04SAndroid Build Coastguard Worker
599*2b949d04SAndroid Build Coastguard Worker    global trials
600*2b949d04SAndroid Build Coastguard Worker    trials = options.trials
601*2b949d04SAndroid Build Coastguard Worker
602*2b949d04SAndroid Build Coastguard Worker    global verbose
603*2b949d04SAndroid Build Coastguard Worker    verbose = options.verbose
604*2b949d04SAndroid Build Coastguard Worker
605*2b949d04SAndroid Build Coastguard Worker    if len(args) not in (1, 2):
606*2b949d04SAndroid Build Coastguard Worker        parser.error("incorrect number of arguments")
607*2b949d04SAndroid Build Coastguard Worker
608*2b949d04SAndroid Build Coastguard Worker    if len(args) == 2 and not args[1].count('tmpl'):
609*2b949d04SAndroid Build Coastguard Worker        parser.error("template filename does not contain 'tmpl'")
610*2b949d04SAndroid Build Coastguard Worker
611*2b949d04SAndroid Build Coastguard Worker    if options.hft == 1:
612*2b949d04SAndroid Build Coastguard Worker        Hash = StrSaltHash
613*2b949d04SAndroid Build Coastguard Worker    elif options.hft == 2:
614*2b949d04SAndroid Build Coastguard Worker        Hash = IntSaltHash
615*2b949d04SAndroid Build Coastguard Worker    else:
616*2b949d04SAndroid Build Coastguard Worker        parser.error("Hash function %s not implemented." % options.hft)
617*2b949d04SAndroid Build Coastguard Worker
618*2b949d04SAndroid Build Coastguard Worker    # --------------------- end parsing and checking --------------
619*2b949d04SAndroid Build Coastguard Worker
620*2b949d04SAndroid Build Coastguard Worker    keys_file = args[0]
621*2b949d04SAndroid Build Coastguard Worker
622*2b949d04SAndroid Build Coastguard Worker    if verbose:
623*2b949d04SAndroid Build Coastguard Worker        print("keys_file = %r" % keys_file)
624*2b949d04SAndroid Build Coastguard Worker
625*2b949d04SAndroid Build Coastguard Worker    keys = read_table(keys_file, options)
626*2b949d04SAndroid Build Coastguard Worker
627*2b949d04SAndroid Build Coastguard Worker    if verbose:
628*2b949d04SAndroid Build Coastguard Worker        print("Number os keys: %d" % len(keys))
629*2b949d04SAndroid Build Coastguard Worker
630*2b949d04SAndroid Build Coastguard Worker    tmpl_file = args[1] if len(args) == 2 else None
631*2b949d04SAndroid Build Coastguard Worker
632*2b949d04SAndroid Build Coastguard Worker    if verbose:
633*2b949d04SAndroid Build Coastguard Worker        print("tmpl_file = %r" % tmpl_file)
634*2b949d04SAndroid Build Coastguard Worker
635*2b949d04SAndroid Build Coastguard Worker    template = read_template(tmpl_file) if tmpl_file else None
636*2b949d04SAndroid Build Coastguard Worker
637*2b949d04SAndroid Build Coastguard Worker    if options.output:
638*2b949d04SAndroid Build Coastguard Worker        outname = options.output
639*2b949d04SAndroid Build Coastguard Worker    else:
640*2b949d04SAndroid Build Coastguard Worker        if tmpl_file:
641*2b949d04SAndroid Build Coastguard Worker            if 'tmpl' not in tmpl_file:
642*2b949d04SAndroid Build Coastguard Worker                sys.exit("Hmm, template filename does not contain 'tmpl'")
643*2b949d04SAndroid Build Coastguard Worker            outname = tmpl_file.replace('tmpl', 'code')
644*2b949d04SAndroid Build Coastguard Worker        else:
645*2b949d04SAndroid Build Coastguard Worker            outname = 'std'
646*2b949d04SAndroid Build Coastguard Worker
647*2b949d04SAndroid Build Coastguard Worker    if verbose:
648*2b949d04SAndroid Build Coastguard Worker        print("outname = %r\n" % outname)
649*2b949d04SAndroid Build Coastguard Worker
650*2b949d04SAndroid Build Coastguard Worker    if outname == 'std':
651*2b949d04SAndroid Build Coastguard Worker        outstream = sys.stdout
652*2b949d04SAndroid Build Coastguard Worker    elif outname == 'no':
653*2b949d04SAndroid Build Coastguard Worker        outstream = None
654*2b949d04SAndroid Build Coastguard Worker    else:
655*2b949d04SAndroid Build Coastguard Worker        try:
656*2b949d04SAndroid Build Coastguard Worker            outstream = open(outname, 'w')
657*2b949d04SAndroid Build Coastguard Worker        except IOError:
658*2b949d04SAndroid Build Coastguard Worker            sys.exit("Error: Could not open `%s' for writing." % outname)
659*2b949d04SAndroid Build Coastguard Worker
660*2b949d04SAndroid Build Coastguard Worker    code = generate_code(keys, Hash, template, options)
661*2b949d04SAndroid Build Coastguard Worker
662*2b949d04SAndroid Build Coastguard Worker    if options.execute or template == builtin_template(Hash):
663*2b949d04SAndroid Build Coastguard Worker        if verbose:
664*2b949d04SAndroid Build Coastguard Worker            print('Executing code...\n')
665*2b949d04SAndroid Build Coastguard Worker        run_code(code)
666*2b949d04SAndroid Build Coastguard Worker
667*2b949d04SAndroid Build Coastguard Worker    if outstream:
668*2b949d04SAndroid Build Coastguard Worker        outstream.write(code)
669*2b949d04SAndroid Build Coastguard Worker        if not outname == 'std':
670*2b949d04SAndroid Build Coastguard Worker            outstream.close()
671*2b949d04SAndroid Build Coastguard Worker
672*2b949d04SAndroid Build Coastguard Worker
673*2b949d04SAndroid Build Coastguard Workerif __name__ == '__main__':
674*2b949d04SAndroid Build Coastguard Worker    main()
675