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