1#!/usr/bin/env python3
2
3# Copyright 2017 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
17from __future__ import print_function
18
19import collections
20import sys
21
22import perfection
23
24_MAX_HEADER_LIST_SIZE = 16 * 1024 * 1024
25
26Setting = collections.namedtuple('Setting', 'id default min max on_error')
27OnError = collections.namedtuple('OnError', 'behavior code')
28clamp_invalid_value = OnError('CLAMP_INVALID_VALUE', 'PROTOCOL_ERROR')
29disconnect_on_invalid_value = lambda e: OnError('DISCONNECT_ON_INVALID_VALUE', e
30                                               )
31DecoratedSetting = collections.namedtuple('DecoratedSetting',
32                                          'enum name setting')
33
34_SETTINGS = {
35    'HEADER_TABLE_SIZE':
36        Setting(1, 4096, 0, 0xffffffff, clamp_invalid_value),
37    'ENABLE_PUSH':
38        Setting(2, 1, 0, 1, disconnect_on_invalid_value('PROTOCOL_ERROR')),
39    'MAX_CONCURRENT_STREAMS':
40        Setting(3, 0xffffffff, 0, 0xffffffff,
41                disconnect_on_invalid_value('PROTOCOL_ERROR')),
42    'INITIAL_WINDOW_SIZE':
43        Setting(4, 65535, 0, 0x7fffffff,
44                disconnect_on_invalid_value('FLOW_CONTROL_ERROR')),
45    'MAX_FRAME_SIZE':
46        Setting(5, 16384, 16384, 16777215,
47                disconnect_on_invalid_value('PROTOCOL_ERROR')),
48    'MAX_HEADER_LIST_SIZE':
49        Setting(6, _MAX_HEADER_LIST_SIZE, 0, _MAX_HEADER_LIST_SIZE,
50                clamp_invalid_value),
51    'GRPC_ALLOW_TRUE_BINARY_METADATA':
52        Setting(0xfe03, 0, 0, 1, clamp_invalid_value),
53    'GRPC_PREFERRED_RECEIVE_CRYPTO_FRAME_SIZE':
54        Setting(0xfe04, 0, 16384, 0x7fffffff, clamp_invalid_value),
55}
56
57H = open('src/core/ext/transport/chttp2/transport/http2_settings.h', 'w')
58C = open('src/core/ext/transport/chttp2/transport/http2_settings.cc', 'w')
59
60
61# utility: print a big comment block into a set of files
62def put_banner(files, banner):
63    for f in files:
64        print('/*', file=f)
65        for line in banner:
66            print(' * %s' % line, file=f)
67        print(' */', file=f)
68        print(file=f)
69
70
71# copy-paste copyright notice from this file
72with open(sys.argv[0]) as my_source:
73    copyright = []
74    for line in my_source:
75        if line[0] != '#':
76            break
77    for line in my_source:
78        if line[0] == '#':
79            copyright.append(line)
80            break
81    for line in my_source:
82        if line[0] != '#':
83            break
84        copyright.append(line)
85    put_banner([H, C], [line[2:].rstrip() for line in copyright])
86
87put_banner(
88    [H, C],
89    ["Automatically generated by tools/codegen/core/gen_settings_ids.py"])
90
91print("#ifndef GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H",
92      file=H)
93print("#define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H",
94      file=H)
95print(file=H)
96print("#include <grpc/support/port_platform.h>", file=H)
97print("#include <stdint.h>", file=H)
98print(file=H)
99
100print("#include <grpc/support/port_platform.h>", file=C)
101print("#include \"src/core/ext/transport/chttp2/transport/http2_settings.h\"",
102      file=C)
103print(file=C)
104print("#include \"src/core/lib/gpr/useful.h\"", file=C)
105print("#include \"src/core/lib/transport/http2_errors.h\"", file=C)
106print(file=C)
107
108p = perfection.hash_parameters(sorted(x.id for x in list(_SETTINGS.values())))
109print(p)
110
111
112def hash(i):
113    i += p.offset
114    x = i % p.t
115    y = i // p.t
116    return x + p.r[y]
117
118
119decorated_settings = [
120    DecoratedSetting(hash(setting.id), name, setting)
121    for name, setting in _SETTINGS.items()
122]
123
124print('typedef enum {', file=H)
125for decorated_setting in sorted(decorated_settings):
126    print('  GRPC_CHTTP2_SETTINGS_%s = %d, /* wire id %d */' %
127          (decorated_setting.name, decorated_setting.enum,
128           decorated_setting.setting.id),
129          file=H)
130print('} grpc_chttp2_setting_id;', file=H)
131print(file=H)
132print('#define GRPC_CHTTP2_NUM_SETTINGS %d' %
133      (max(x.enum for x in decorated_settings) + 1),
134      file=H)
135
136print('extern const uint16_t grpc_setting_id_to_wire_id[];', file=H)
137print('const uint16_t grpc_setting_id_to_wire_id[] = {%s};' %
138      ','.join('%d' % s for s in p.slots),
139      file=C)
140print(file=H)
141print(
142    "bool grpc_wire_id_to_setting_id(uint32_t wire_id, grpc_chttp2_setting_id *out);",
143    file=H)
144cgargs = {
145    'r': ','.join('%d' % (r if r is not None else 0) for r in p.r),
146    't': p.t,
147    'offset': abs(p.offset),
148    'offset_sign': '+' if p.offset > 0 else '-'
149}
150print("""
151bool grpc_wire_id_to_setting_id(uint32_t wire_id, grpc_chttp2_setting_id *out) {
152  uint32_t i = wire_id %(offset_sign)s %(offset)d;
153  uint32_t x = i %% %(t)d;
154  uint32_t y = i / %(t)d;
155  uint32_t h = x;
156  switch (y) {
157""" % cgargs,
158      file=C)
159for i, r in enumerate(p.r):
160    if not r:
161        continue
162    if r < 0:
163        print('case %d: h -= %d; break;' % (i, -r), file=C)
164    else:
165        print('case %d: h += %d; break;' % (i, r), file=C)
166print("""
167  }
168  *out = static_cast<grpc_chttp2_setting_id>(h);
169  return h < GPR_ARRAY_SIZE(grpc_setting_id_to_wire_id) && grpc_setting_id_to_wire_id[h] == wire_id;
170}
171""" % cgargs,
172      file=C)
173
174print("""
175typedef enum {
176  GRPC_CHTTP2_CLAMP_INVALID_VALUE,
177  GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE
178} grpc_chttp2_invalid_value_behavior;
179
180typedef struct {
181  const char *name;
182  uint32_t default_value;
183  uint32_t min_value;
184  uint32_t max_value;
185  grpc_chttp2_invalid_value_behavior invalid_value_behavior;
186  uint32_t error_value;
187} grpc_chttp2_setting_parameters;
188
189extern const grpc_chttp2_setting_parameters grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS];
190""",
191      file=H)
192print(
193    "const grpc_chttp2_setting_parameters grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS] = {",
194    file=C)
195i = 0
196for decorated_setting in sorted(decorated_settings):
197    while i < decorated_setting.enum:
198        print(
199            "{NULL, 0, 0, 0, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR},",
200            file=C)
201        i += 1
202    print("{\"%s\", %du, %du, %du, GRPC_CHTTP2_%s, GRPC_HTTP2_%s}," % (
203        decorated_setting.name,
204        decorated_setting.setting.default,
205        decorated_setting.setting.min,
206        decorated_setting.setting.max,
207        decorated_setting.setting.on_error.behavior,
208        decorated_setting.setting.on_error.code,
209    ),
210          file=C)
211    i += 1
212print("};", file=C)
213
214print(file=H)
215print("#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H */",
216      file=H)
217
218H.close()
219C.close()
220