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