Home | History | Annotate | Download | only in core
      1 #!/usr/bin/env python2.7
      2 
      3 # Copyright 2015 gRPC authors.
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 #     http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 
     17 import hashlib
     18 import itertools
     19 import collections
     20 import os
     21 import sys
     22 import subprocess
     23 import re
     24 import perfection
     25 
     26 # Configuration: a list of either strings or 2-tuples of strings or 3-tuples of
     27 # strings.
     28 # A single string represents a static grpc_mdstr.
     29 # A 2-tuple represents a static grpc_mdelem (and appropriate grpc_mdstrs will
     30 # also be created).
     31 # A 3-tuple represents a static grpc_mdelem (and appropriate grpc_mdstrs will
     32 # also be created), with the last value equivalent to the mdelem's static hpack
     33 # table index as defined by RFC 7541
     34 
     35 CONFIG = [
     36     # metadata strings
     37     'host',
     38     'grpc-timeout',
     39     'grpc-internal-encoding-request',
     40     'grpc-internal-stream-encoding-request',
     41     'grpc-payload-bin',
     42     ':path',
     43     'grpc-encoding',
     44     'grpc-accept-encoding',
     45     'user-agent',
     46     ':authority',
     47     'grpc-message',
     48     'grpc-status',
     49     'grpc-server-stats-bin',
     50     'grpc-tags-bin',
     51     'grpc-trace-bin',
     52     'grpc-previous-rpc-attempts',
     53     'grpc-retry-pushback-ms',
     54     '1',
     55     '2',
     56     '3',
     57     '4',
     58     '',
     59     # channel arg keys
     60     'grpc.wait_for_ready',
     61     'grpc.timeout',
     62     'grpc.max_request_message_bytes',
     63     'grpc.max_response_message_bytes',
     64     # well known method names
     65     '/grpc.lb.v1.LoadBalancer/BalanceLoad',
     66     # compression algorithm names
     67     'deflate',
     68     'gzip',
     69     'stream/gzip',
     70     # metadata elements
     71     ('grpc-status', '0'),
     72     ('grpc-status', '1'),
     73     ('grpc-status', '2'),
     74     ('grpc-encoding', 'identity'),
     75     ('grpc-encoding', 'gzip'),
     76     ('grpc-encoding', 'deflate'),
     77     ('te', 'trailers'),
     78     ('content-type', 'application/grpc'),
     79     (':method', 'POST', 3),
     80     (':status', '200', 8),
     81     (':status', '404', 13),
     82     (':scheme', 'http', 6),
     83     (':scheme', 'https', 7),
     84     (':scheme', 'grpc', 0),
     85     (':authority', '', 1),
     86     (':method', 'GET', 2),
     87     (':method', 'PUT'),
     88     (':path', '/', 4),
     89     (':path', '/index.html', 5),
     90     (':status', '204', 9),
     91     (':status', '206', 10),
     92     (':status', '304', 11),
     93     (':status', '400', 12),
     94     (':status', '500', 14),
     95     ('accept-charset', '', 15),
     96     ('accept-encoding', ''),
     97     ('accept-encoding', 'gzip, deflate', 16),
     98     ('accept-language', '', 17),
     99     ('accept-ranges', '', 18),
    100     ('accept', '', 19),
    101     ('access-control-allow-origin', '', 20),
    102     ('age', '', 21),
    103     ('allow', '', 22),
    104     ('authorization', '', 23),
    105     ('cache-control', '', 24),
    106     ('content-disposition', '', 25),
    107     ('content-encoding', 'identity'),
    108     ('content-encoding', 'gzip'),
    109     ('content-encoding', '', 26),
    110     ('content-language', '', 27),
    111     ('content-length', '', 28),
    112     ('content-location', '', 29),
    113     ('content-range', '', 30),
    114     ('content-type', '', 31),
    115     ('cookie', '', 32),
    116     ('date', '', 33),
    117     ('etag', '', 34),
    118     ('expect', '', 35),
    119     ('expires', '', 36),
    120     ('from', '', 37),
    121     ('host', '', 38),
    122     ('if-match', '', 39),
    123     ('if-modified-since', '', 40),
    124     ('if-none-match', '', 41),
    125     ('if-range', '', 42),
    126     ('if-unmodified-since', '', 43),
    127     ('last-modified', '', 44),
    128     ('lb-token', ''),
    129     ('lb-cost-bin', ''),
    130     ('link', '', 45),
    131     ('location', '', 46),
    132     ('max-forwards', '', 47),
    133     ('proxy-authenticate', '', 48),
    134     ('proxy-authorization', '', 49),
    135     ('range', '', 50),
    136     ('referer', '', 51),
    137     ('refresh', '', 52),
    138     ('retry-after', '', 53),
    139     ('server', '', 54),
    140     ('set-cookie', '', 55),
    141     ('strict-transport-security', '', 56),
    142     ('transfer-encoding', '', 57),
    143     ('user-agent', '', 58),
    144     ('vary', '', 59),
    145     ('via', '', 60),
    146     ('www-authenticate', '', 61),
    147 ]
    148 
    149 # All entries here are ignored when counting non-default initial metadata that
    150 # prevents the chttp2 server from sending a Trailers-Only response.
    151 METADATA_BATCH_CALLOUTS = [
    152     # (name)
    153     (':path'),
    154     (':method'),
    155     (':status'),
    156     (':authority'),
    157     (':scheme'),
    158     ('te'),
    159     ('grpc-message'),
    160     ('grpc-status'),
    161     ('grpc-payload-bin'),
    162     ('grpc-encoding'),
    163     ('grpc-accept-encoding'),
    164     ('grpc-server-stats-bin'),
    165     ('grpc-tags-bin'),
    166     ('grpc-trace-bin'),
    167     ('content-type'),
    168     ('content-encoding'),
    169     ('accept-encoding'),
    170     ('grpc-internal-encoding-request'),
    171     ('grpc-internal-stream-encoding-request'),
    172     ('user-agent'),
    173     ('host'),
    174     ('lb-token'),
    175     ('grpc-previous-rpc-attempts'),
    176     ('grpc-retry-pushback-ms'),
    177 ]
    178 
    179 COMPRESSION_ALGORITHMS = [
    180     'identity',
    181     'deflate',
    182     'gzip',
    183 ]
    184 
    185 STREAM_COMPRESSION_ALGORITHMS = [
    186     'identity',
    187     'gzip',
    188 ]
    189 
    190 
    191 # utility: mangle the name of a config
    192 def mangle(elem, name=None):
    193     xl = {
    194         '-': '_',
    195         ':': '',
    196         '/': 'slash',
    197         '.': 'dot',
    198         ',': 'comma',
    199         ' ': '_',
    200     }
    201 
    202     def m0(x):
    203         if not x:
    204             return 'empty'
    205         r = ''
    206         for c in x:
    207             put = xl.get(c, c.lower())
    208             if not put:
    209                 continue
    210             last_is_underscore = r[-1] == '_' if r else True
    211             if last_is_underscore and put == '_':
    212                 continue
    213             elif len(put) > 1:
    214                 if not last_is_underscore:
    215                     r += '_'
    216                 r += put
    217                 r += '_'
    218             else:
    219                 r += put
    220         if r[-1] == '_':
    221             r = r[:-1]
    222         return r
    223 
    224     def n(default, name=name):
    225         if name is None:
    226             return 'grpc_%s_' % default
    227         if name == '':
    228             return ''
    229         return 'grpc_%s_' % name
    230 
    231     if isinstance(elem, tuple):
    232         return '%s%s_%s' % (n('mdelem'), m0(elem[0]), m0(elem[1]))
    233     else:
    234         return '%s%s' % (n('mdstr'), m0(elem))
    235 
    236 
    237 # utility: generate some hash value for a string
    238 def fake_hash(elem):
    239     return hashlib.md5(elem).hexdigest()[0:8]
    240 
    241 
    242 # utility: print a big comment block into a set of files
    243 def put_banner(files, banner):
    244     for f in files:
    245         print >> f, '/*'
    246         for line in banner:
    247             print >> f, ' * %s' % line
    248         print >> f, ' */'
    249         print >> f
    250 
    251 
    252 # build a list of all the strings we need
    253 all_strs = list()
    254 all_elems = list()
    255 static_userdata = {}
    256 # put metadata batch callouts first, to make the check of if a static metadata
    257 # string is a callout trivial
    258 for elem in METADATA_BATCH_CALLOUTS:
    259     if elem not in all_strs:
    260         all_strs.append(elem)
    261 for elem in CONFIG:
    262     if isinstance(elem, tuple):
    263         if elem[0] not in all_strs:
    264             all_strs.append(elem[0])
    265         if elem[1] not in all_strs:
    266             all_strs.append(elem[1])
    267         if elem not in all_elems:
    268             all_elems.append(elem)
    269     else:
    270         if elem not in all_strs:
    271             all_strs.append(elem)
    272 compression_elems = []
    273 for mask in range(1, 1 << len(COMPRESSION_ALGORITHMS)):
    274     val = ','.join(COMPRESSION_ALGORITHMS[alg]
    275                    for alg in range(0, len(COMPRESSION_ALGORITHMS))
    276                    if (1 << alg) & mask)
    277     elem = ('grpc-accept-encoding', val)
    278     if val not in all_strs:
    279         all_strs.append(val)
    280     if elem not in all_elems:
    281         all_elems.append(elem)
    282     compression_elems.append(elem)
    283     static_userdata[elem] = 1 + (mask | 1)
    284 stream_compression_elems = []
    285 for mask in range(1, 1 << len(STREAM_COMPRESSION_ALGORITHMS)):
    286     val = ','.join(STREAM_COMPRESSION_ALGORITHMS[alg]
    287                    for alg in range(0, len(STREAM_COMPRESSION_ALGORITHMS))
    288                    if (1 << alg) & mask)
    289     elem = ('accept-encoding', val)
    290     if val not in all_strs:
    291         all_strs.append(val)
    292     if elem not in all_elems:
    293         all_elems.append(elem)
    294     stream_compression_elems.append(elem)
    295     static_userdata[elem] = 1 + (mask | 1)
    296 
    297 # output configuration
    298 args = sys.argv[1:]
    299 H = None
    300 C = None
    301 D = None
    302 if args:
    303     if 'header' in args:
    304         H = sys.stdout
    305     else:
    306         H = open('/dev/null', 'w')
    307     if 'source' in args:
    308         C = sys.stdout
    309     else:
    310         C = open('/dev/null', 'w')
    311     if 'dictionary' in args:
    312         D = sys.stdout
    313     else:
    314         D = open('/dev/null', 'w')
    315 else:
    316     H = open(
    317         os.path.join(
    318             os.path.dirname(sys.argv[0]),
    319             '../../../src/core/lib/transport/static_metadata.h'), 'w')
    320     C = open(
    321         os.path.join(
    322             os.path.dirname(sys.argv[0]),
    323             '../../../src/core/lib/transport/static_metadata.cc'), 'w')
    324     D = open(
    325         os.path.join(
    326             os.path.dirname(sys.argv[0]),
    327             '../../../test/core/end2end/fuzzers/hpack.dictionary'), 'w')
    328 
    329 HPACK_H = open(
    330     os.path.join(
    331         os.path.dirname(sys.argv[0]),
    332         '../../../src/core/ext/transport/chttp2/transport/hpack_mapping.h'),
    333     'w')
    334 HPACK_C = open(
    335     os.path.join(
    336         os.path.dirname(sys.argv[0]),
    337         '../../../src/core/ext/transport/chttp2/transport/hpack_mapping.cc'),
    338     'w')
    339 
    340 # copy-paste copyright notice from this file
    341 with open(sys.argv[0]) as my_source:
    342     copyright = []
    343     for line in my_source:
    344         if line[0] != '#':
    345             break
    346     for line in my_source:
    347         if line[0] == '#':
    348             copyright.append(line)
    349             break
    350     for line in my_source:
    351         if line[0] != '#':
    352             break
    353         copyright.append(line)
    354     put_banner([H, C, HPACK_H, HPACK_C],
    355                [line[2:].rstrip() for line in copyright])
    356 
    357 hex_bytes = [ord(c) for c in 'abcdefABCDEF0123456789']
    358 
    359 
    360 def esc_dict(line):
    361     out = "\""
    362     for c in line:
    363         if 32 <= c < 127:
    364             if c != ord('"'):
    365                 out += chr(c)
    366             else:
    367                 out += "\\\""
    368         else:
    369             out += '\\x%02X' % c
    370     return out + "\""
    371 
    372 
    373 put_banner([H, C], """WARNING: Auto-generated code.
    374 
    375 To make changes to this file, change
    376 tools/codegen/core/gen_static_metadata.py, and then re-run it.
    377 
    378 See metadata.h for an explanation of the interface here, and metadata.cc for
    379 an explanation of what's going on.
    380 """.splitlines())
    381 
    382 put_banner([HPACK_H, HPACK_C], """WARNING: Auto-generated code.
    383 
    384 To make changes to this file, change
    385 tools/codegen/core/gen_static_metadata.py, and then re-run it.
    386 
    387 This file contains the mapping from the index of each metadata element in the 
    388 grpc static metadata table to the index of that element in the hpack static 
    389 metadata table. If the element is not contained in the static hpack table, then
    390 the returned index is 0.
    391 """.splitlines())
    392 
    393 print >> H, '#ifndef GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H'
    394 print >> H, '#define GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H'
    395 print >> H
    396 print >> H, '#include <grpc/support/port_platform.h>'
    397 print >> H
    398 print >> H, '#include "src/core/lib/transport/metadata.h"'
    399 print >> H
    400 print >> C, '#include <grpc/support/port_platform.h>'
    401 print >> C
    402 print >> C, '#include "src/core/lib/transport/static_metadata.h"'
    403 print >> C
    404 print >> C, '#include "src/core/lib/slice/slice_internal.h"'
    405 print >> C
    406 print >> HPACK_H, ('#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_'
    407                    'MAPPING_H')
    408 print >> HPACK_H, ('#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_'
    409                    'MAPPING_H')
    410 print >> HPACK_H
    411 print >> HPACK_H, '#include <grpc/support/port_platform.h>'
    412 print >> HPACK_H
    413 print >> HPACK_H, '#include "src/core/lib/transport/static_metadata.h"'
    414 print >> HPACK_H
    415 print >> HPACK_C, '#include <grpc/support/port_platform.h>'
    416 print >> HPACK_C
    417 print >> HPACK_C, ('#include '
    418                    '"src/core/ext/transport/chttp2/transport/hpack_mapping.h"')
    419 print >> HPACK_C
    420 
    421 str_ofs = 0
    422 id2strofs = {}
    423 for i, elem in enumerate(all_strs):
    424     id2strofs[i] = str_ofs
    425     str_ofs += len(elem)
    426 
    427 
    428 def slice_def(i):
    429     return ('{&grpc_static_metadata_refcounts[%d],'
    430             ' {{g_bytes+%d, %d}}}') % (i, id2strofs[i], len(all_strs[i]))
    431 
    432 
    433 # validate configuration
    434 for elem in METADATA_BATCH_CALLOUTS:
    435     assert elem in all_strs
    436 
    437 print >> H, '#define GRPC_STATIC_MDSTR_COUNT %d' % len(all_strs)
    438 print >> H, ('extern const grpc_slice '
    439              'grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT];')
    440 for i, elem in enumerate(all_strs):
    441     print >> H, '/* "%s" */' % elem
    442     print >> H, '#define %s (grpc_static_slice_table[%d])' % (
    443         mangle(elem).upper(), i)
    444 print >> H
    445 print >> C, 'static uint8_t g_bytes[] = {%s};' % (','.join(
    446     '%d' % ord(c) for c in ''.join(all_strs)))
    447 print >> C
    448 print >> C, 'static void static_ref(void *unused) {}'
    449 print >> C, 'static void static_unref(void *unused) {}'
    450 print >> C, ('static const grpc_slice_refcount_vtable static_sub_vtable = '
    451              '{static_ref, static_unref, grpc_slice_default_eq_impl, '
    452              'grpc_slice_default_hash_impl};')
    453 print >> H, ('extern const grpc_slice_refcount_vtable '
    454              'grpc_static_metadata_vtable;')
    455 print >> C, ('const grpc_slice_refcount_vtable grpc_static_metadata_vtable = '
    456              '{static_ref, static_unref, grpc_static_slice_eq, '
    457              'grpc_static_slice_hash};')
    458 print >> C, ('static grpc_slice_refcount static_sub_refcnt = '
    459              '{&static_sub_vtable, &static_sub_refcnt};')
    460 print >> H, ('extern grpc_slice_refcount '
    461              'grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT];')
    462 print >> C, ('grpc_slice_refcount '
    463              'grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT] = {')
    464 for i, elem in enumerate(all_strs):
    465     print >> C, '  {&grpc_static_metadata_vtable, &static_sub_refcnt},'
    466 print >> C, '};'
    467 print >> C
    468 print >> H, '#define GRPC_IS_STATIC_METADATA_STRING(slice) \\'
    469 print >> H, ('  ((slice).refcount != NULL && (slice).refcount->vtable == '
    470              '&grpc_static_metadata_vtable)')
    471 print >> H
    472 print >> C, ('const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT]'
    473              ' = {')
    474 for i, elem in enumerate(all_strs):
    475     print >> C, slice_def(i) + ','
    476 print >> C, '};'
    477 print >> C
    478 print >> H, '#define GRPC_STATIC_METADATA_INDEX(static_slice) \\'
    479 print >> H, ('  ((int)((static_slice).refcount - '
    480              'grpc_static_metadata_refcounts))')
    481 print >> H
    482 
    483 print >> D, '# hpack fuzzing dictionary'
    484 for i, elem in enumerate(all_strs):
    485     print >> D, '%s' % (esc_dict([len(elem)] + [ord(c) for c in elem]))
    486 for i, elem in enumerate(all_elems):
    487     print >> D, '%s' % (esc_dict([0, len(elem[0])] + [ord(c) for c in elem[0]] +
    488                                  [len(elem[1])] + [ord(c) for c in elem[1]]))
    489 
    490 print >> H, '#define GRPC_STATIC_MDELEM_COUNT %d' % len(all_elems)
    491 print >> H, ('extern grpc_mdelem_data '
    492              'grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];')
    493 print >> H, ('extern uintptr_t '
    494              'grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT];')
    495 for i, elem in enumerate(all_elems):
    496     print >> H, '/* "%s": "%s" */' % (elem[0], elem[1])
    497     print >> H, ('#define %s (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[%d], '
    498                  'GRPC_MDELEM_STORAGE_STATIC))') % (mangle(elem).upper(), i)
    499 print >> H
    500 
    501 # Print out the chttp2 mapping between static mdelem index and the hpack static
    502 # table index
    503 print >> HPACK_H, ('extern const uint8_t grpc_hpack_static_mdelem_indices['
    504                    'GRPC_STATIC_MDELEM_COUNT];')
    505 print >> HPACK_H
    506 print >> HPACK_C, ('const uint8_t grpc_hpack_static_mdelem_indices['
    507                    'GRPC_STATIC_MDELEM_COUNT] = {')
    508 indices = ''
    509 for elem in all_elems:
    510     index = 0
    511     if len(elem) == 3:
    512         index = elem[2]
    513     indices += '%d,' % index
    514 print >> HPACK_C, '  %s' % indices
    515 print >> HPACK_C, '};'
    516 print >> HPACK_C
    517 
    518 print >> C, ('uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] '
    519              '= {')
    520 print >> C, '  %s' % ','.join(
    521     '%d' % static_userdata.get(elem, 0) for elem in all_elems)
    522 print >> C, '};'
    523 print >> C
    524 
    525 
    526 def str_idx(s):
    527     for i, s2 in enumerate(all_strs):
    528         if s == s2:
    529             return i
    530 
    531 
    532 def md_idx(m):
    533     for i, m2 in enumerate(all_elems):
    534         if m == m2:
    535             return i
    536 
    537 
    538 def offset_trials(mink):
    539     yield 0
    540     for i in range(1, 100):
    541         for mul in [-1, 1]:
    542             yield mul * i
    543 
    544 
    545 def perfect_hash(keys, name):
    546     p = perfection.hash_parameters(keys)
    547 
    548     def f(i, p=p):
    549         i += p.offset
    550         x = i % p.t
    551         y = i / p.t
    552         return x + p.r[y]
    553 
    554     return {
    555         'PHASHRANGE': p.t - 1 + max(p.r),
    556         'PHASHNKEYS': len(p.slots),
    557         'pyfunc': f,
    558         'code': """
    559 static const int8_t %(name)s_r[] = {%(r)s};
    560 static uint32_t %(name)s_phash(uint32_t i) {
    561   i %(offset_sign)s= %(offset)d;
    562   uint32_t x = i %% %(t)d;
    563   uint32_t y = i / %(t)d;
    564   uint32_t h = x;
    565   if (y < GPR_ARRAY_SIZE(%(name)s_r)) {
    566     uint32_t delta = (uint32_t)%(name)s_r[y];
    567     h += delta;
    568   }
    569   return h;
    570 }
    571     """ % {
    572             'name': name,
    573             'r': ','.join('%d' % (r if r is not None else 0) for r in p.r),
    574             't': p.t,
    575             'offset': abs(p.offset),
    576             'offset_sign': '+' if p.offset > 0 else '-'
    577         }
    578     }
    579 
    580 
    581 elem_keys = [
    582     str_idx(elem[0]) * len(all_strs) + str_idx(elem[1]) for elem in all_elems
    583 ]
    584 elem_hash = perfect_hash(elem_keys, 'elems')
    585 print >> C, elem_hash['code']
    586 
    587 keys = [0] * int(elem_hash['PHASHRANGE'])
    588 idxs = [255] * int(elem_hash['PHASHNKEYS'])
    589 for i, k in enumerate(elem_keys):
    590     h = elem_hash['pyfunc'](k)
    591     assert keys[h] == 0
    592     keys[h] = k
    593     idxs[h] = i
    594 print >> C, 'static const uint16_t elem_keys[] = {%s};' % ','.join(
    595     '%d' % k for k in keys)
    596 print >> C, 'static const uint8_t elem_idxs[] = {%s};' % ','.join(
    597     '%d' % i for i in idxs)
    598 print >> C
    599 
    600 print >> H, 'grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b);'
    601 print >> C, 'grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b) {'
    602 print >> C, '  if (a == -1 || b == -1) return GRPC_MDNULL;'
    603 print >> C, '  uint32_t k = (uint32_t)(a * %d + b);' % len(all_strs)
    604 print >> C, '  uint32_t h = elems_phash(k);'
    605 print >> C, '  return h < GPR_ARRAY_SIZE(elem_keys) && elem_keys[h] == k && elem_idxs[h] != 255 ? GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[elem_idxs[h]], GRPC_MDELEM_STORAGE_STATIC) : GRPC_MDNULL;'
    606 print >> C, '}'
    607 print >> C
    608 
    609 print >> C, 'grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT] = {'
    610 for elem in all_elems:
    611     print >> C, '{%s,%s},' % (slice_def(str_idx(elem[0])),
    612                               slice_def(str_idx(elem[1])))
    613 print >> C, '};'
    614 
    615 print >> H, 'typedef enum {'
    616 for elem in METADATA_BATCH_CALLOUTS:
    617     print >> H, '  %s,' % mangle(elem, 'batch').upper()
    618 print >> H, '  GRPC_BATCH_CALLOUTS_COUNT'
    619 print >> H, '} grpc_metadata_batch_callouts_index;'
    620 print >> H
    621 print >> H, 'typedef union {'
    622 print >> H, '  struct grpc_linked_mdelem *array[GRPC_BATCH_CALLOUTS_COUNT];'
    623 print >> H, '  struct {'
    624 for elem in METADATA_BATCH_CALLOUTS:
    625     print >> H, '  struct grpc_linked_mdelem *%s;' % mangle(elem, '').lower()
    626 print >> H, '  } named;'
    627 print >> H, '} grpc_metadata_batch_callouts;'
    628 print >> H
    629 print >> H, '#define GRPC_BATCH_INDEX_OF(slice) \\'
    630 print >> H, '  (GRPC_IS_STATIC_METADATA_STRING((slice)) ? (grpc_metadata_batch_callouts_index)GPR_CLAMP(GRPC_STATIC_METADATA_INDEX((slice)), 0, GRPC_BATCH_CALLOUTS_COUNT) : GRPC_BATCH_CALLOUTS_COUNT)'
    631 print >> H
    632 
    633 print >> H, 'extern const uint8_t grpc_static_accept_encoding_metadata[%d];' % (
    634     1 << len(COMPRESSION_ALGORITHMS))
    635 print >> C, 'const uint8_t grpc_static_accept_encoding_metadata[%d] = {' % (
    636     1 << len(COMPRESSION_ALGORITHMS))
    637 print >> C, '0,%s' % ','.join('%d' % md_idx(elem) for elem in compression_elems)
    638 print >> C, '};'
    639 print >> C
    640 
    641 print >> H, '#define GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(algs) (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[grpc_static_accept_encoding_metadata[(algs)]], GRPC_MDELEM_STORAGE_STATIC))'
    642 print >> H
    643 
    644 print >> H, 'extern const uint8_t grpc_static_accept_stream_encoding_metadata[%d];' % (
    645     1 << len(STREAM_COMPRESSION_ALGORITHMS))
    646 print >> C, 'const uint8_t grpc_static_accept_stream_encoding_metadata[%d] = {' % (
    647     1 << len(STREAM_COMPRESSION_ALGORITHMS))
    648 print >> C, '0,%s' % ','.join(
    649     '%d' % md_idx(elem) for elem in stream_compression_elems)
    650 print >> C, '};'
    651 
    652 print >> H, '#define GRPC_MDELEM_ACCEPT_STREAM_ENCODING_FOR_ALGORITHMS(algs) (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[grpc_static_accept_stream_encoding_metadata[(algs)]], GRPC_MDELEM_STORAGE_STATIC))'
    653 
    654 print >> H, '#endif /* GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H */'
    655 
    656 print >> HPACK_H, ('#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_'
    657                    'MAPPING_H */')
    658 
    659 H.close()
    660 C.close()
    661