1#!/usr/bin/env python3
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
17import collections
18import hashlib
19import itertools
20import os
21import re
22import subprocess
23import sys
24
25import perfection
26
27# Configuration: a list of either strings or 2-tuples of 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# The list of 2-tuples must begin with the static hpack table elements as
32# defined by RFC 7541 and be in the same order because of an hpack encoding
33# performance optimization that relies on this. If you want to change this, then
34# you must change the implementation of the encoding optimization as well.
35
36CONFIG = [
37    # metadata strings
38    'host',
39    'grpc-timeout',
40    'grpc-internal-encoding-request',
41    'grpc-internal-stream-encoding-request',
42    'grpc-payload-bin',
43    ':path',
44    'grpc-encoding',
45    'grpc-accept-encoding',
46    'user-agent',
47    ':authority',
48    'grpc-message',
49    'grpc-status',
50    'grpc-server-stats-bin',
51    'grpc-tags-bin',
52    'grpc-trace-bin',
53    'grpc-previous-rpc-attempts',
54    'grpc-retry-pushback-ms',
55    '1',
56    '2',
57    '3',
58    '4',
59    '',
60    'x-endpoint-load-metrics-bin',
61    # channel arg keys
62    'grpc.wait_for_ready',
63    'grpc.timeout',
64    'grpc.max_request_message_bytes',
65    'grpc.max_response_message_bytes',
66    # well known method names
67    '/grpc.lb.v1.LoadBalancer/BalanceLoad',
68    '/envoy.service.load_stats.v2.LoadReportingService/StreamLoadStats',
69    '/envoy.service.load_stats.v3.LoadReportingService/StreamLoadStats',
70    '/grpc.health.v1.Health/Watch',
71    '/envoy.service.discovery.v2.AggregatedDiscoveryService/StreamAggregatedResources',
72    '/envoy.service.discovery.v3.AggregatedDiscoveryService/StreamAggregatedResources',
73    # compression algorithm names
74    'deflate',
75    'gzip',
76    'stream/gzip',
77    # te: trailers strings
78    'te',
79    'trailers',
80    # metadata elements
81    # begin hpack static elements
82    (':authority', ''),
83    (':method', 'GET'),
84    (':method', 'POST'),
85    (':path', '/'),
86    (':path', '/index.html'),
87    (':scheme', 'http'),
88    (':scheme', 'https'),
89    (':status', '200'),
90    (':status', '204'),
91    (':status', '206'),
92    (':status', '304'),
93    (':status', '400'),
94    (':status', '404'),
95    (':status', '500'),
96    ('accept-charset', ''),
97    ('accept-encoding', 'gzip, deflate'),
98    ('accept-language', ''),
99    ('accept-ranges', ''),
100    ('accept', ''),
101    ('access-control-allow-origin', ''),
102    ('age', ''),
103    ('allow', ''),
104    ('authorization', ''),
105    ('cache-control', ''),
106    ('content-disposition', ''),
107    ('content-encoding', ''),
108    ('content-language', ''),
109    ('content-length', ''),
110    ('content-location', ''),
111    ('content-range', ''),
112    ('content-type', ''),
113    ('cookie', ''),
114    ('date', ''),
115    ('etag', ''),
116    ('expect', ''),
117    ('expires', ''),
118    ('from', ''),
119    ('host', ''),
120    ('if-match', ''),
121    ('if-modified-since', ''),
122    ('if-none-match', ''),
123    ('if-range', ''),
124    ('if-unmodified-since', ''),
125    ('last-modified', ''),
126    ('link', ''),
127    ('location', ''),
128    ('max-forwards', ''),
129    ('proxy-authenticate', ''),
130    ('proxy-authorization', ''),
131    ('range', ''),
132    ('referer', ''),
133    ('refresh', ''),
134    ('retry-after', ''),
135    ('server', ''),
136    ('set-cookie', ''),
137    ('strict-transport-security', ''),
138    ('transfer-encoding', ''),
139    ('user-agent', ''),
140    ('vary', ''),
141    ('via', ''),
142    ('www-authenticate', ''),
143    # end hpack static elements
144    ('grpc-status', '0'),
145    ('grpc-status', '1'),
146    ('grpc-status', '2'),
147    ('grpc-encoding', 'identity'),
148    ('grpc-encoding', 'gzip'),
149    ('grpc-encoding', 'deflate'),
150    ('content-type', 'application/grpc'),
151    (':scheme', 'grpc'),
152    (':method', 'PUT'),
153    ('accept-encoding', ''),
154    ('content-encoding', 'identity'),
155    ('content-encoding', 'gzip'),
156    ('lb-cost-bin', ''),
157]
158
159# All entries here are ignored when counting non-default initial metadata that
160# prevents the chttp2 server from sending a Trailers-Only response.
161METADATA_BATCH_CALLOUTS = [
162    ':path',
163    ':method',
164    ':status',
165    ':authority',
166    ':scheme',
167    'grpc-message',
168    'grpc-status',
169    'grpc-payload-bin',
170    'grpc-encoding',
171    'grpc-accept-encoding',
172    'grpc-server-stats-bin',
173    'grpc-tags-bin',
174    'grpc-trace-bin',
175    'content-type',
176    'content-encoding',
177    'accept-encoding',
178    'grpc-internal-encoding-request',
179    'grpc-internal-stream-encoding-request',
180    'user-agent',
181    'host',
182    'grpc-previous-rpc-attempts',
183    'grpc-retry-pushback-ms',
184    'x-endpoint-load-metrics-bin',
185]
186
187COMPRESSION_ALGORITHMS = [
188    'identity',
189    'deflate',
190    'gzip',
191]
192
193STREAM_COMPRESSION_ALGORITHMS = [
194    'identity',
195    'gzip',
196]
197
198
199# utility: mangle the name of a config
200def mangle(elem, name=None):
201    xl = {
202        '-': '_',
203        ':': '',
204        '/': 'slash',
205        '.': 'dot',
206        ',': 'comma',
207        ' ': '_',
208    }
209
210    def m0(x):
211        if not x:
212            return 'empty'
213        r = ''
214        for c in x:
215            put = xl.get(c, c.lower())
216            if not put:
217                continue
218            last_is_underscore = r[-1] == '_' if r else True
219            if last_is_underscore and put == '_':
220                continue
221            elif len(put) > 1:
222                if not last_is_underscore:
223                    r += '_'
224                r += put
225                r += '_'
226            else:
227                r += put
228        if r[-1] == '_':
229            r = r[:-1]
230        return r
231
232    def n(default, name=name):
233        if name is None:
234            return 'grpc_%s_' % default
235        if name == '':
236            return ''
237        return 'grpc_%s_' % name
238
239    if isinstance(elem, tuple):
240        return '%s%s_%s' % (n('mdelem'), m0(elem[0]), m0(elem[1]))
241    else:
242        return '%s%s' % (n('mdstr'), m0(elem))
243
244
245# utility: generate some hash value for a string
246def fake_hash(elem):
247    return hashlib.md5(elem).hexdigest()[0:8]
248
249
250# utility: print a big comment block into a set of files
251def put_banner(files, banner):
252    for f in files:
253        print('/*', file=f)
254        for line in banner:
255            print(' * %s' % line, file=f)
256        print(' */', file=f)
257        print('', file=f)
258
259
260# build a list of all the strings we need
261all_strs = list()
262all_elems = list()
263static_userdata = {}
264# put metadata batch callouts first, to make the check of if a static metadata
265# string is a callout trivial
266for elem in METADATA_BATCH_CALLOUTS:
267    if elem not in all_strs:
268        all_strs.append(elem)
269for elem in CONFIG:
270    if isinstance(elem, tuple):
271        if elem[0] not in all_strs:
272            all_strs.append(elem[0])
273        if elem[1] not in all_strs:
274            all_strs.append(elem[1])
275        if elem not in all_elems:
276            all_elems.append(elem)
277    else:
278        if elem not in all_strs:
279            all_strs.append(elem)
280compression_elems = []
281for mask in range(1, 1 << len(COMPRESSION_ALGORITHMS)):
282    val = ','.join(COMPRESSION_ALGORITHMS[alg]
283                   for alg in range(0, len(COMPRESSION_ALGORITHMS))
284                   if (1 << alg) & mask)
285    elem = ('grpc-accept-encoding', val)
286    if val not in all_strs:
287        all_strs.append(val)
288    if elem not in all_elems:
289        all_elems.append(elem)
290    compression_elems.append(elem)
291    static_userdata[elem] = 1 + (mask | 1)
292stream_compression_elems = []
293for mask in range(1, 1 << len(STREAM_COMPRESSION_ALGORITHMS)):
294    val = ','.join(STREAM_COMPRESSION_ALGORITHMS[alg]
295                   for alg in range(0, len(STREAM_COMPRESSION_ALGORITHMS))
296                   if (1 << alg) & mask)
297    elem = ('accept-encoding', val)
298    if val not in all_strs:
299        all_strs.append(val)
300    if elem not in all_elems:
301        all_elems.append(elem)
302    stream_compression_elems.append(elem)
303    static_userdata[elem] = 1 + (mask | 1)
304
305# output configuration
306args = sys.argv[1:]
307MD_H = None
308MD_C = None
309STR_H = None
310STR_C = None
311D = None
312if args:
313    if 'md_header' in args:
314        MD_H = sys.stdout
315    else:
316        MD_H = open('/dev/null', 'w')
317    if 'md_source' in args:
318        MD_C = sys.stdout
319    else:
320        MD_C = open('/dev/null', 'w')
321    if 'str_header' in args:
322        STR_H = sys.stdout
323    else:
324        STR_H = open('/dev/null', 'w')
325    if 'str_source' in args:
326        STR_C = sys.stdout
327    else:
328        STR_C = open('/dev/null', 'w')
329    if 'dictionary' in args:
330        D = sys.stdout
331    else:
332        D = open('/dev/null', 'w')
333else:
334    MD_H = open(
335        os.path.join(os.path.dirname(sys.argv[0]),
336                     '../../../src/core/lib/transport/static_metadata.h'), 'w')
337    MD_C = open(
338        os.path.join(os.path.dirname(sys.argv[0]),
339                     '../../../src/core/lib/transport/static_metadata.cc'), 'w')
340    STR_H = open(
341        os.path.join(os.path.dirname(sys.argv[0]),
342                     '../../../src/core/lib/slice/static_slice.h'), 'w')
343    STR_C = open(
344        os.path.join(os.path.dirname(sys.argv[0]),
345                     '../../../src/core/lib/slice/static_slice.cc'), 'w')
346    D = open(
347        os.path.join(os.path.dirname(sys.argv[0]),
348                     '../../../test/core/end2end/fuzzers/hpack.dictionary'),
349        'w')
350
351# copy-paste copyright notice from this file
352with open(sys.argv[0]) as my_source:
353    copyright = []
354    for line in my_source:
355        if line[0] != '#':
356            break
357    for line in my_source:
358        if line[0] == '#':
359            copyright.append(line)
360            break
361    for line in my_source:
362        if line[0] != '#':
363            break
364        copyright.append(line)
365    put_banner([MD_H, MD_C, STR_H, STR_C],
366               [line[2:].rstrip() for line in copyright])
367
368hex_bytes = [ord(c) for c in 'abcdefABCDEF0123456789']
369
370
371def esc_dict(line):
372    out = "\""
373    for c in line:
374        if 32 <= c < 127:
375            if c != ord('"'):
376                out += chr(c)
377            else:
378                out += "\\\""
379        else:
380            out += '\\x%02X' % c
381    return out + "\""
382
383
384put_banner([MD_H, MD_C, STR_H, STR_C], """WARNING: Auto-generated code.
385
386To make changes to this file, change
387tools/codegen/core/gen_static_metadata.py, and then re-run it.
388
389See metadata.h for an explanation of the interface here, and metadata.cc for
390an explanation of what's going on.
391""".splitlines())
392
393print('#ifndef GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H', file=MD_H)
394print('#define GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H', file=MD_H)
395print('', file=MD_H)
396print('#include <grpc/support/port_platform.h>', file=MD_H)
397print('', file=MD_H)
398print('#include <cstdint>', file=MD_H)
399print('', file=MD_H)
400print('#include "src/core/lib/transport/metadata.h"', file=MD_H)
401print('#include "src/core/lib/slice/static_slice.h"', file=MD_H)
402print('', file=MD_H)
403print('#ifndef GRPC_CORE_LIB_SLICE_STATIC_SLICE_H', file=STR_H)
404print('#define GRPC_CORE_LIB_SLICE_STATIC_SLICE_H', file=STR_H)
405print('', file=STR_H)
406print('#include <grpc/support/port_platform.h>', file=STR_H)
407print('', file=STR_H)
408print('#include <cstdint>', file=STR_H)
409print('#include <type_traits>', file=STR_H)
410print('#include "src/core/lib/slice/slice_utils.h"', file=STR_H)
411print('#include "src/core/lib/slice/slice_refcount_base.h"', file=STR_H)
412print('', file=STR_H)
413print('#include <grpc/support/port_platform.h>', file=MD_C)
414print('', file=MD_C)
415print('#include "src/core/lib/transport/static_metadata.h"', file=MD_C)
416print('', file=MD_C)
417print('#include "src/core/lib/slice/slice_internal.h"', file=MD_C)
418print('', file=MD_C)
419print('#include <grpc/support/port_platform.h>', file=STR_C)
420print('', file=STR_C)
421print('#include "src/core/lib/slice/static_slice.h"', file=STR_C)
422print('', file=STR_C)
423
424str_ofs = 0
425id2strofs = {}
426for i, elem in enumerate(all_strs):
427    id2strofs[i] = str_ofs
428    str_ofs += len(elem)
429
430
431def slice_def_for_ctx(i):
432    return (
433        'grpc_core::StaticMetadataSlice(&g_static_metadata_slice_refcounts[%d].base, %d, g_static_metadata_bytes+%d)'
434    ) % (i, len(all_strs[i]), id2strofs[i])
435
436
437def slice_def(i):
438    return (
439        'grpc_core::StaticMetadataSlice(&g_static_metadata_slice_refcounts[%d].base, %d, g_static_metadata_bytes+%d)'
440    ) % (i, len(all_strs[i]), id2strofs[i])
441
442
443def str_idx(s):
444    for i, s2 in enumerate(all_strs):
445        if s == s2:
446            return i
447
448
449# validate configuration
450for elem in METADATA_BATCH_CALLOUTS:
451    assert elem in all_strs
452static_slice_dest_assert = (
453    'static_assert(std::is_trivially_destructible' +
454    '<grpc_core::StaticMetadataSlice>::value, '
455    '"grpc_core::StaticMetadataSlice must be trivially destructible.");')
456print(static_slice_dest_assert, file=STR_H)
457print('#define GRPC_STATIC_MDSTR_COUNT %d' % len(all_strs), file=STR_H)
458for i, elem in enumerate(all_strs):
459    print('/* "%s" */' % elem, file=STR_H)
460    print('#define %s (::grpc_core::g_static_metadata_slice_table[%d])' %
461          (mangle(elem).upper(), i),
462          file=STR_H)
463print('', file=STR_H)
464print('namespace grpc_core {', file=STR_C)
465print('',
466      'const uint8_t g_static_metadata_bytes[] = {%s};' %
467      (','.join('%d' % ord(c) for c in ''.join(all_strs))),
468      file=STR_C)
469print('', file=STR_C)
470print('''
471namespace grpc_core {
472extern StaticSliceRefcount g_static_metadata_slice_refcounts[GRPC_STATIC_MDSTR_COUNT];
473extern const StaticMetadataSlice g_static_metadata_slice_table[GRPC_STATIC_MDSTR_COUNT];
474extern const uint8_t g_static_metadata_bytes[];
475}
476''',
477      file=STR_H)
478print('grpc_slice_refcount grpc_core::StaticSliceRefcount::kStaticSubRefcount;',
479      file=STR_C)
480print('''
481StaticSliceRefcount
482    g_static_metadata_slice_refcounts[GRPC_STATIC_MDSTR_COUNT] = {
483''',
484      file=STR_C)
485for i, elem in enumerate(all_strs):
486    print('  StaticSliceRefcount(%d), ' % i, file=STR_C)
487print('};', file=STR_C)  # static slice refcounts
488print('', file=STR_C)
489print('''
490  const StaticMetadataSlice
491    g_static_metadata_slice_table[GRPC_STATIC_MDSTR_COUNT] = {
492''',
493      file=STR_C)
494for i, elem in enumerate(all_strs):
495    print(slice_def_for_ctx(i) + ',', file=STR_C)
496print('};', file=STR_C)  # static slices
497print('namespace grpc_core {', file=MD_C)
498print('StaticMetadata g_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT] = {',
499      file=MD_C)
500for idx, (a, b) in enumerate(all_elems):
501    print('StaticMetadata(%s,%s, %d),' %
502          (slice_def_for_ctx(str_idx(a)), slice_def_for_ctx(str_idx(b)), idx),
503          file=MD_C)
504print('};', file=MD_C)  # static_mdelem_table
505print(('''
506/* Warning: the core static metadata currently operates under the soft constraint
507that the first GRPC_CHTTP2_LAST_STATIC_ENTRY (61) entries must contain
508metadata specified by the http2 hpack standard. The CHTTP2 transport reads the
509core metadata with this assumption in mind. If the order of the core static
510metadata is to be changed, then the CHTTP2 transport must be changed as well to
511stop relying on the core metadata. */
512'''),
513      file=MD_C)
514print(('grpc_mdelem '
515       'g_static_mdelem_manifested[GRPC_STATIC_MDELEM_COUNT] = {'),
516      file=MD_C)
517print('// clang-format off', file=MD_C)
518static_mds = []
519for i, elem in enumerate(all_elems):
520    md_name = mangle(elem).upper()
521    md_human_readable = '"%s": "%s"' % elem
522    md_spec = '    /* %s: \n     %s */\n' % (md_name, md_human_readable)
523    md_spec += '    GRPC_MAKE_MDELEM(\n'
524    md_spec += (('        &g_static_mdelem_table[%d].data(),\n' % i) +
525                '        GRPC_MDELEM_STORAGE_STATIC)')
526    static_mds.append(md_spec)
527print(',\n'.join(static_mds), file=MD_C)
528print('// clang-format on', file=MD_C)
529print(('};'), file=MD_C)  # static_mdelem_manifested
530print('}', file=MD_C)  # namespace grpc_core
531print('}', file=STR_C)  # namespace grpc_core
532
533print('', file=MD_C)
534print('#define GRPC_IS_STATIC_METADATA_STRING(slice) \\', file=STR_H)
535print(('  ((slice).refcount != NULL && (slice).refcount->GetType() == '
536       'grpc_slice_refcount::Type::STATIC)'),
537      file=STR_H)
538print('', file=STR_H)
539print('', file=STR_C)
540print('#define GRPC_STATIC_METADATA_INDEX(static_slice) \\', file=STR_H)
541print(
542    '(reinterpret_cast<grpc_core::StaticSliceRefcount*>((static_slice).refcount)->index)',
543    file=STR_H)
544print('', file=STR_H)
545
546print('# hpack fuzzing dictionary', file=D)
547for i, elem in enumerate(all_strs):
548    print('%s' % (esc_dict([len(elem)] + [ord(c) for c in elem])), file=D)
549for i, elem in enumerate(all_elems):
550    print('%s' % (esc_dict([0, len(elem[0])] + [ord(c) for c in elem[0]] +
551                           [len(elem[1])] + [ord(c) for c in elem[1]])),
552          file=D)
553
554print('#define GRPC_STATIC_MDELEM_COUNT %d' % len(all_elems), file=MD_H)
555print('''
556namespace grpc_core {
557extern StaticMetadata g_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT];
558extern grpc_mdelem g_static_mdelem_manifested[GRPC_STATIC_MDELEM_COUNT];
559}
560''',
561      file=MD_H)
562print(('extern uintptr_t '
563       'grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT];'),
564      file=MD_H)
565
566for i, elem in enumerate(all_elems):
567    md_name = mangle(elem).upper()
568    print('/* "%s": "%s" */' % elem, file=MD_H)
569    print(('#define %s (::grpc_core::g_static_mdelem_manifested[%d])' %
570           (md_name, i)),
571          file=MD_H)
572print('', file=MD_H)
573
574print(('uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] '
575       '= {'),
576      file=MD_C)
577print('  %s' %
578      ','.join('%d' % static_userdata.get(elem, 0) for elem in all_elems),
579      file=MD_C)
580print('};', file=MD_C)
581print('', file=MD_C)
582
583
584def md_idx(m):
585    for i, m2 in enumerate(all_elems):
586        if m == m2:
587            return i
588
589
590def offset_trials(mink):
591    yield 0
592    for i in range(1, 100):
593        for mul in [-1, 1]:
594            yield mul * i
595
596
597def perfect_hash(keys, name):
598    p = perfection.hash_parameters(keys)
599
600    def f(i, p=p):
601        i += p.offset
602        x = i % p.t
603        y = i // p.t
604        return x + p.r[y]
605
606    return {
607        'PHASHNKEYS':
608            len(p.slots),
609        'pyfunc':
610            f,
611        'code':
612            """
613static const int8_t %(name)s_r[] = {%(r)s};
614static uint32_t %(name)s_phash(uint32_t i) {
615  i %(offset_sign)s= %(offset)d;
616  uint32_t x = i %% %(t)d;
617  uint32_t y = i / %(t)d;
618  uint32_t h = x;
619  if (y < GPR_ARRAY_SIZE(%(name)s_r)) {
620    uint32_t delta = static_cast<uint32_t>(%(name)s_r[y]);
621    h += delta;
622  }
623  return h;
624}
625    """ % {
626                'name': name,
627                'r': ','.join('%d' % (r if r is not None else 0) for r in p.r),
628                't': p.t,
629                'offset': abs(p.offset),
630                'offset_sign': '+' if p.offset > 0 else '-'
631            }
632    }
633
634
635elem_keys = [
636    str_idx(elem[0]) * len(all_strs) + str_idx(elem[1]) for elem in all_elems
637]
638elem_hash = perfect_hash(elem_keys, 'elems')
639print(elem_hash['code'], file=MD_C)
640
641keys = [0] * int(elem_hash['PHASHNKEYS'])
642idxs = [255] * int(elem_hash['PHASHNKEYS'])
643for i, k in enumerate(elem_keys):
644    h = elem_hash['pyfunc'](k)
645    assert keys[h] == 0
646    keys[h] = k
647    idxs[h] = i
648print('static const uint16_t elem_keys[] = {%s};' %
649      ','.join('%d' % k for k in keys),
650      file=MD_C)
651print('static const uint8_t elem_idxs[] = {%s};' %
652      ','.join('%d' % i for i in idxs),
653      file=MD_C)
654print('', file=MD_C)
655
656print(
657    'grpc_mdelem grpc_static_mdelem_for_static_strings(intptr_t a, intptr_t b);',
658    file=MD_H)
659print(
660    'grpc_mdelem grpc_static_mdelem_for_static_strings(intptr_t a, intptr_t b) {',
661    file=MD_C)
662print('  if (a == -1 || b == -1) return GRPC_MDNULL;', file=MD_C)
663print('  uint32_t k = static_cast<uint32_t>(a * %d + b);' % len(all_strs),
664      file=MD_C)
665print('  uint32_t h = elems_phash(k);', file=MD_C)
666print(
667    '  return h < GPR_ARRAY_SIZE(elem_keys) && elem_keys[h] == k && elem_idxs[h] != 255 ? GRPC_MAKE_MDELEM(&grpc_core::g_static_mdelem_table[elem_idxs[h]].data(), GRPC_MDELEM_STORAGE_STATIC) : GRPC_MDNULL;',
668    file=MD_C)
669print('}', file=MD_C)
670print('', file=MD_C)
671
672print('typedef enum {', file=MD_H)
673for elem in METADATA_BATCH_CALLOUTS:
674    print('  %s,' % mangle(elem, 'batch').upper(), file=MD_H)
675print('  GRPC_BATCH_CALLOUTS_COUNT', file=MD_H)
676print('} grpc_metadata_batch_callouts_index;', file=MD_H)
677print('', file=MD_H)
678print('typedef union {', file=MD_H)
679print('  struct grpc_linked_mdelem *array[GRPC_BATCH_CALLOUTS_COUNT];',
680      file=MD_H)
681print('  struct {', file=MD_H)
682for elem in METADATA_BATCH_CALLOUTS:
683    print('  struct grpc_linked_mdelem *%s;' % mangle(elem, '').lower(),
684          file=MD_H)
685print('  } named;', file=MD_H)
686print('} grpc_metadata_batch_callouts;', file=MD_H)
687print('', file=MD_H)
688
689batch_idx_of_hdr = '#define GRPC_BATCH_INDEX_OF(slice) \\'
690static_slice = 'GRPC_IS_STATIC_METADATA_STRING((slice))'
691slice_to_slice_ref = '(slice).refcount'
692static_slice_ref_type = 'grpc_core::StaticSliceRefcount*'
693slice_ref_as_static = ('reinterpret_cast<' + static_slice_ref_type + '>(' +
694                       slice_to_slice_ref + ')')
695slice_ref_idx = slice_ref_as_static + '->index'
696batch_idx_type = 'grpc_metadata_batch_callouts_index'
697slice_ref_idx_to_batch_idx = ('static_cast<' + batch_idx_type + '>(' +
698                              slice_ref_idx + ')')
699batch_invalid_idx = 'GRPC_BATCH_CALLOUTS_COUNT'
700batch_invalid_u32 = 'static_cast<uint32_t>(' + batch_invalid_idx + ')'
701# Assemble GRPC_BATCH_INDEX_OF(slice) macro as a join for ease of reading.
702batch_idx_of_pieces = [
703    batch_idx_of_hdr, '\n', '(', static_slice, '&&', slice_ref_idx, '<=',
704    batch_invalid_u32, '?', slice_ref_idx_to_batch_idx, ':', batch_invalid_idx,
705    ')'
706]
707print(''.join(batch_idx_of_pieces), file=MD_H)
708print('', file=MD_H)
709
710print('extern const uint8_t grpc_static_accept_encoding_metadata[%d];' %
711      (1 << len(COMPRESSION_ALGORITHMS)),
712      file=MD_H)
713print('const uint8_t grpc_static_accept_encoding_metadata[%d] = {' %
714      (1 << len(COMPRESSION_ALGORITHMS)),
715      file=MD_C)
716print('0,%s' % ','.join('%d' % md_idx(elem) for elem in compression_elems),
717      file=MD_C)
718print('};', file=MD_C)
719print('', file=MD_C)
720
721print(
722    '#define GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(algs) (GRPC_MAKE_MDELEM(&grpc_core::g_static_mdelem_table[grpc_static_accept_encoding_metadata[(algs)]].data(), GRPC_MDELEM_STORAGE_STATIC))',
723    file=MD_H)
724print('', file=MD_H)
725
726print('extern const uint8_t grpc_static_accept_stream_encoding_metadata[%d];' %
727      (1 << len(STREAM_COMPRESSION_ALGORITHMS)),
728      file=MD_H)
729print('const uint8_t grpc_static_accept_stream_encoding_metadata[%d] = {' %
730      (1 << len(STREAM_COMPRESSION_ALGORITHMS)),
731      file=MD_C)
732print('0,%s' %
733      ','.join('%d' % md_idx(elem) for elem in stream_compression_elems),
734      file=MD_C)
735print('};', file=MD_C)
736
737print(
738    '#define GRPC_MDELEM_ACCEPT_STREAM_ENCODING_FOR_ALGORITHMS(algs) (GRPC_MAKE_MDELEM(&grpc_core::g_static_mdelem_table[grpc_static_accept_stream_encoding_metadata[(algs)]].data(), GRPC_MDELEM_STORAGE_STATIC))',
739    file=MD_H)
740
741print('#endif /* GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H */', file=MD_H)
742print('#endif /* GRPC_CORE_LIB_SLICE_STATIC_SLICE_H */', file=STR_H)
743
744MD_H.close()
745MD_C.close()
746STR_H.close()
747STR_C.close()
748