| # Copyright 2016 The Chromium Authors. All rights reserved. | 
 | # Use of this source code is governed by a BSD-style license that can be | 
 | # found in the LICENSE file. | 
 |  | 
 | from __future__ import print_function | 
 |  | 
 | import os | 
 | import struct | 
 | import sys | 
 |  | 
 | def Main(args): | 
 |   if len(args) < 4: | 
 |     print( | 
 |         "Usage: %s output.hmap Foo.framework header1.h..." % args[0], | 
 |         file=sys.stderr) | 
 |     return 1 | 
 |  | 
 |   (out, framework, all_headers) = args[1], args[2], args[3:] | 
 |  | 
 |   framework_name = os.path.basename(framework).split('.')[0] | 
 |   all_headers = map(os.path.abspath, all_headers) | 
 |   filelist = {} | 
 |   for header in all_headers: | 
 |     filename = os.path.basename(header) | 
 |     filelist[filename] = header | 
 |     filelist[os.path.join(framework_name, filename)] = header | 
 |   WriteHmap(out, filelist) | 
 |   return 0 | 
 |  | 
 |  | 
 | def NextGreaterPowerOf2(x): | 
 |   return 2**(x).bit_length() | 
 |  | 
 |  | 
 | def WriteHmap(output_name, filelist): | 
 |   """Generates a header map based on |filelist|. | 
 |  | 
 |   Per Mark Mentovai: | 
 |     A header map is structured essentially as a hash table, keyed by names used | 
 |     in #includes, and providing pathnames to the actual files. | 
 |  | 
 |   The implementation below and the comment above comes from inspecting: | 
 |     http://www.opensource.apple.com/source/distcc/distcc-2503/distcc_dist/include_server/headermap.py?txt | 
 |   while also looking at the implementation in clang in: | 
 |     https://llvm.org/svn/llvm-project/cfe/trunk/lib/Lex/HeaderMap.cpp | 
 |   """ | 
 |   magic = 1751998832 | 
 |   version = 1 | 
 |   _reserved = 0 | 
 |   count = len(filelist) | 
 |   capacity = NextGreaterPowerOf2(count) | 
 |   strings_offset = 24 + (12 * capacity) | 
 |   max_value_length = len(max(filelist.values(), key=lambda v: len(v))) | 
 |  | 
 |   out = open(output_name, 'wb') | 
 |   out.write(struct.pack('<LHHLLLL', magic, version, _reserved, strings_offset, | 
 |                         count, capacity, max_value_length)) | 
 |  | 
 |   # Create empty hashmap buckets. | 
 |   buckets = [None] * capacity | 
 |   for file, path in filelist.items(): | 
 |     key = 0 | 
 |     for c in file: | 
 |       key += ord(c.lower()) * 13 | 
 |  | 
 |     # Fill next empty bucket. | 
 |     while buckets[key & capacity - 1] is not None: | 
 |       key = key + 1 | 
 |     buckets[key & capacity - 1] = (file, path) | 
 |  | 
 |   next_offset = 1 | 
 |   for bucket in buckets: | 
 |     if bucket is None: | 
 |       out.write(struct.pack('<LLL', 0, 0, 0)) | 
 |     else: | 
 |       (file, path) = bucket | 
 |       key_offset = next_offset | 
 |       prefix_offset = key_offset + len(file) + 1 | 
 |       suffix_offset = prefix_offset + len(os.path.dirname(path) + os.sep) + 1 | 
 |       next_offset = suffix_offset + len(os.path.basename(path)) + 1 | 
 |       out.write(struct.pack('<LLL', key_offset, prefix_offset, suffix_offset)) | 
 |  | 
 |   # Pad byte since next offset starts at 1. | 
 |   out.write(struct.pack('<x')) | 
 |  | 
 |   for bucket in buckets: | 
 |     if bucket is not None: | 
 |       (file, path) = bucket | 
 |       base = os.path.dirname(path) + os.sep | 
 |       path = os.path.basename(path) | 
 |       file = file.encode('UTF-8') | 
 |       base = base.encode('UTF-8') | 
 |       path = path.encode('UTF-8') | 
 |       out.write(struct.pack('<%ds' % len(file), file)) | 
 |       out.write(struct.pack('<s', b'\0')) | 
 |       out.write(struct.pack('<%ds' % len(base), base)) | 
 |       out.write(struct.pack('<s', b'\0')) | 
 |       out.write(struct.pack('<%ds' % len(path), path)) | 
 |       out.write(struct.pack('<s', b'\0')) | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |   sys.exit(Main(sys.argv)) |