aboutsummaryrefslogtreecommitdiff
path: root/src/core/lib/transport/metadata_batch.h
blob: ea833c8cbaa1519c350ec1489cc13035d4159aaa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
//
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//

#ifndef GRPC_SRC_CORE_LIB_TRANSPORT_METADATA_BATCH_H
#define GRPC_SRC_CORE_LIB_TRANSPORT_METADATA_BATCH_H

#include <grpc/support/port_platform.h>

#include <stdlib.h>

#include <cstdint>
#include <string>
#include <type_traits>
#include <utility>

#include "absl/container/inlined_vector.h"
#include "absl/functional/function_ref.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/numbers.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"

#include <grpc/impl/compression_types.h>
#include <grpc/status.h>
#include <grpc/support/log.h>

#include "src/core/lib/compression/compression_internal.h"
#include "src/core/lib/experiments/experiments.h"
#include "src/core/lib/gprpp/chunked_vector.h"
#include "src/core/lib/gprpp/if_list.h"
#include "src/core/lib/gprpp/packed_table.h"
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/gprpp/type_list.h"
#include "src/core/lib/promise/poll.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/transport/custom_metadata.h"
#include "src/core/lib/transport/metadata_compression_traits.h"
#include "src/core/lib/transport/parsed_metadata.h"
#include "src/core/lib/transport/simple_slice_based_metadata.h"

namespace grpc_core {

///////////////////////////////////////////////////////////////////////////////
// Metadata traits

// Given a metadata key and a value, return the encoded size.
// Defaults to calling the key's Encode() method and then calculating the size
// of that, but can be overridden for specific keys if there's a better way of
// doing this.
// May return 0 if the size is unknown/unknowable.
template <typename Key>
size_t EncodedSizeOfKey(Key, const typename Key::ValueType& value) {
  return Key::Encode(value).size();
}

// grpc-timeout metadata trait.
// ValueType is defined as Timestamp - an absolute timestamp (i.e. a
// deadline!), that is converted to a duration by transports before being
// sent.
// TODO(ctiller): Move this elsewhere. During the transition we need to be able
// to name this in MetadataMap, but ultimately once the transition is done we
// should not need to.
struct GrpcTimeoutMetadata {
  static constexpr bool kRepeatable = false;
  using ValueType = Timestamp;
  using MementoType = Duration;
  using CompressionTraits = TimeoutCompressor;
  static absl::string_view key() { return "grpc-timeout"; }
  static MementoType ParseMemento(Slice value,
                                  bool will_keep_past_request_lifetime,
                                  MetadataParseErrorFn on_error);
  static ValueType MementoToValue(MementoType timeout);
  static Slice Encode(ValueType x);
  static std::string DisplayValue(ValueType x) { return x.ToString(); }
  static std::string DisplayMemento(MementoType x) { return x.ToString(); }
};

// TE metadata trait.
struct TeMetadata {
  static constexpr bool kRepeatable = false;
  // HTTP2 says that TE can either be empty or "trailers".
  // Empty means this trait is not included, "trailers" means kTrailers, and
  // kInvalid is used to remember an invalid value.
  enum ValueType : uint8_t {
    kTrailers,
    kInvalid,
  };
  using MementoType = ValueType;
  using CompressionTraits = KnownValueCompressor<ValueType, kTrailers>;
  static absl::string_view key() { return "te"; }
  static MementoType ParseMemento(Slice value,
                                  bool will_keep_past_request_lifetime,
                                  MetadataParseErrorFn on_error);
  static ValueType MementoToValue(MementoType te) { return te; }
  static StaticSlice Encode(ValueType x) {
    GPR_ASSERT(x == kTrailers);
    return StaticSlice::FromStaticString("trailers");
  }
  static const char* DisplayValue(ValueType te);
  static const char* DisplayMemento(MementoType te) { return DisplayValue(te); }
};

inline size_t EncodedSizeOfKey(TeMetadata, TeMetadata::ValueType x) {
  return x == TeMetadata::kTrailers ? 8 : 0;
}

// content-type metadata trait.
struct ContentTypeMetadata {
  static constexpr bool kRepeatable = false;
  // gRPC says that content-type can be application/grpc[;something]
  // Core has only ever verified the prefix.
  // IF we want to start verifying more, we can expand this type.
  enum ValueType : uint8_t {
    kApplicationGrpc,
    kEmpty,
    kInvalid,
  };
  using MementoType = ValueType;
  using CompressionTraits = KnownValueCompressor<ValueType, kApplicationGrpc>;
  static absl::string_view key() { return "content-type"; }
  static MementoType ParseMemento(Slice value,
                                  bool will_keep_past_request_lifetime,
                                  MetadataParseErrorFn on_error);
  static ValueType MementoToValue(MementoType content_type) {
    return content_type;
  }

  static StaticSlice Encode(ValueType x);
  static const char* DisplayValue(ValueType content_type);
  static const char* DisplayMemento(ValueType content_type) {
    return DisplayValue(content_type);
  }
};

// scheme metadata trait.
struct HttpSchemeMetadata {
  static constexpr bool kRepeatable = false;
  enum ValueType : uint8_t {
    kHttp,
    kHttps,
    kInvalid,
  };
  using MementoType = ValueType;
  using CompressionTraits = HttpSchemeCompressor;
  static absl::string_view key() { return ":scheme"; }
  static MementoType ParseMemento(Slice value, bool,
                                  MetadataParseErrorFn on_error) {
    return Parse(value.as_string_view(), on_error);
  }
  static ValueType Parse(absl::string_view value,
                         MetadataParseErrorFn on_error);
  static ValueType MementoToValue(MementoType content_type) {
    return content_type;
  }
  static StaticSlice Encode(ValueType x);
  static const char* DisplayValue(ValueType content_type);
  static const char* DisplayMemento(MementoType content_type) {
    return DisplayValue(content_type);
  }
};

size_t EncodedSizeOfKey(HttpSchemeMetadata, HttpSchemeMetadata::ValueType x);

// method metadata trait.
struct HttpMethodMetadata {
  static constexpr bool kRepeatable = false;
  enum ValueType : uint8_t {
    kPost,
    kGet,
    kPut,
    kInvalid,
  };
  using MementoType = ValueType;
  using CompressionTraits = HttpMethodCompressor;
  static absl::string_view key() { return ":method"; }
  static MementoType ParseMemento(Slice value,
                                  bool will_keep_past_request_lifetime,
                                  MetadataParseErrorFn on_error);
  static ValueType MementoToValue(MementoType content_type) {
    return content_type;
  }
  static StaticSlice Encode(ValueType x);
  static const char* DisplayValue(ValueType content_type);
  static const char* DisplayMemento(MementoType content_type) {
    return DisplayValue(content_type);
  }
};

// Base type for metadata pertaining to a single compression algorithm
// (e.g., "grpc-encoding").
struct CompressionAlgorithmBasedMetadata {
  using ValueType = grpc_compression_algorithm;
  using MementoType = ValueType;
  static MementoType ParseMemento(Slice value,
                                  bool will_keep_past_request_lifetime,
                                  MetadataParseErrorFn on_error);
  static ValueType MementoToValue(MementoType x) { return x; }
  static Slice Encode(ValueType x) {
    GPR_ASSERT(x != GRPC_COMPRESS_ALGORITHMS_COUNT);
    return Slice::FromStaticString(CompressionAlgorithmAsString(x));
  }
  static const char* DisplayValue(ValueType x) {
    if (const char* p = CompressionAlgorithmAsString(x)) {
      return p;
    } else {
      return "<discarded-invalid-value>";
    }
  }
  static const char* DisplayMemento(MementoType x) { return DisplayValue(x); }
};

// grpc-encoding metadata trait.
struct GrpcEncodingMetadata : public CompressionAlgorithmBasedMetadata {
  static constexpr bool kRepeatable = false;
  using CompressionTraits =
      SmallIntegralValuesCompressor<GRPC_COMPRESS_ALGORITHMS_COUNT>;
  static absl::string_view key() { return "grpc-encoding"; }
};

// grpc-internal-encoding-request metadata trait.
struct GrpcInternalEncodingRequest : public CompressionAlgorithmBasedMetadata {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = NoCompressionCompressor;
  static absl::string_view key() { return "grpc-internal-encoding-request"; }
};

// grpc-accept-encoding metadata trait.
struct GrpcAcceptEncodingMetadata {
  static constexpr bool kRepeatable = false;
  static absl::string_view key() { return "grpc-accept-encoding"; }
  using ValueType = CompressionAlgorithmSet;
  using MementoType = ValueType;
  using CompressionTraits = StableValueCompressor;
  static MementoType ParseMemento(Slice value, bool, MetadataParseErrorFn) {
    return CompressionAlgorithmSet::FromString(value.as_string_view());
  }
  static ValueType MementoToValue(MementoType x) { return x; }
  static Slice Encode(ValueType x) { return x.ToSlice(); }
  static absl::string_view DisplayValue(ValueType x) { return x.ToString(); }
  static absl::string_view DisplayMemento(MementoType x) {
    return DisplayValue(x);
  }
};

// user-agent metadata trait.
struct UserAgentMetadata : public SimpleSliceBasedMetadata {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = StableValueCompressor;
  static absl::string_view key() { return "user-agent"; }
};

// grpc-message metadata trait.
struct GrpcMessageMetadata : public SimpleSliceBasedMetadata {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = NoCompressionCompressor;
  static absl::string_view key() { return "grpc-message"; }
};

// host metadata trait.
struct HostMetadata : public SimpleSliceBasedMetadata {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = NoCompressionCompressor;
  static absl::string_view key() { return "host"; }
};

// endpoint-load-metrics-bin metadata trait.
struct EndpointLoadMetricsBinMetadata : public SimpleSliceBasedMetadata {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = NoCompressionCompressor;
  static absl::string_view key() { return "endpoint-load-metrics-bin"; }
};

// grpc-server-stats-bin metadata trait.
struct GrpcServerStatsBinMetadata : public SimpleSliceBasedMetadata {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = NoCompressionCompressor;
  static absl::string_view key() { return "grpc-server-stats-bin"; }
};

// grpc-trace-bin metadata trait.
struct GrpcTraceBinMetadata : public SimpleSliceBasedMetadata {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = FrequentKeyWithNoValueCompressionCompressor;
  static absl::string_view key() { return "grpc-trace-bin"; }
};

// grpc-tags-bin metadata trait.
struct GrpcTagsBinMetadata : public SimpleSliceBasedMetadata {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = FrequentKeyWithNoValueCompressionCompressor;
  static absl::string_view key() { return "grpc-tags-bin"; }
};

// XEnvoyPeerMetadata
struct XEnvoyPeerMetadata : public SimpleSliceBasedMetadata {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = StableValueCompressor;
  static absl::string_view key() { return "x-envoy-peer-metadata"; }
};

// :authority metadata trait.
struct HttpAuthorityMetadata : public SimpleSliceBasedMetadata {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = SmallSetOfValuesCompressor;
  static absl::string_view key() { return ":authority"; }
};

// :path metadata trait.
struct HttpPathMetadata : public SimpleSliceBasedMetadata {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = SmallSetOfValuesCompressor;
  static absl::string_view key() { return ":path"; }
};

// We separate SimpleIntBasedMetadata into two pieces: one that does not
// depend on the invalid value, and one that does. This allows the compiler to
// easily see the functions that are shared, and helps reduce code bloat here.
template <typename Int>
struct SimpleIntBasedMetadataBase {
  using ValueType = Int;
  using MementoType = Int;
  static ValueType MementoToValue(MementoType value) { return value; }
  static Slice Encode(ValueType x) { return Slice::FromInt64(x); }
  static Int DisplayValue(ValueType x) { return x; }
  static Int DisplayMemento(MementoType x) { return x; }
};

template <typename Int, Int kInvalidValue>
struct SimpleIntBasedMetadata : public SimpleIntBasedMetadataBase<Int> {
  static constexpr Int invalid_value() { return kInvalidValue; }
  static Int ParseMemento(Slice value, bool, MetadataParseErrorFn on_error) {
    Int out;
    if (!absl::SimpleAtoi(value.as_string_view(), &out)) {
      on_error("not an integer", value);
      out = kInvalidValue;
    }
    return out;
  }
};

// grpc-status metadata trait.
struct GrpcStatusMetadata
    : public SimpleIntBasedMetadata<grpc_status_code, GRPC_STATUS_UNKNOWN> {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = SmallIntegralValuesCompressor<16>;
  static absl::string_view key() { return "grpc-status"; }
};

// grpc-previous-rpc-attempts metadata trait.
struct GrpcPreviousRpcAttemptsMetadata
    : public SimpleIntBasedMetadata<uint32_t, 0> {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = NoCompressionCompressor;
  static absl::string_view key() { return "grpc-previous-rpc-attempts"; }
};

// grpc-retry-pushback-ms metadata trait.
struct GrpcRetryPushbackMsMetadata {
  static constexpr bool kRepeatable = false;
  static absl::string_view key() { return "grpc-retry-pushback-ms"; }
  using ValueType = Duration;
  using MementoType = Duration;
  using CompressionTraits = NoCompressionCompressor;
  static ValueType MementoToValue(MementoType x) { return x; }
  static Slice Encode(Duration x) { return Slice::FromInt64(x.millis()); }
  static int64_t DisplayValue(Duration x) { return x.millis(); }
  static int64_t DisplayMemento(Duration x) { return DisplayValue(x); }
  static Duration ParseMemento(Slice value,
                               bool will_keep_past_request_lifetime,
                               MetadataParseErrorFn on_error);
};

// :status metadata trait.
// TODO(ctiller): consider moving to uint16_t
struct HttpStatusMetadata : public SimpleIntBasedMetadata<uint32_t, 0> {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = HttpStatusCompressor;
  static absl::string_view key() { return ":status"; }
};

// "secret" metadata trait used to pass load balancing token between filters.
// This should not be exposed outside of gRPC core.
class GrpcLbClientStats;

struct GrpcLbClientStatsMetadata {
  static constexpr bool kRepeatable = false;
  static absl::string_view key() { return "grpclb_client_stats"; }
  using ValueType = GrpcLbClientStats*;
  using MementoType = ValueType;
  using CompressionTraits = NoCompressionCompressor;
  static ValueType MementoToValue(MementoType value) { return value; }
  static Slice Encode(ValueType) { abort(); }
  static const char* DisplayValue(ValueType) { return "<internal-lb-stats>"; }
  static const char* DisplayMemento(MementoType) {
    return "<internal-lb-stats>";
  }
  static MementoType ParseMemento(Slice, bool, MetadataParseErrorFn error) {
    error("not a valid value for grpclb_client_stats", Slice());
    return nullptr;
  }
};

inline size_t EncodedSizeOfKey(GrpcLbClientStatsMetadata,
                               GrpcLbClientStatsMetadata::ValueType) {
  return 0;
}

// lb-token metadata
struct LbTokenMetadata : public SimpleSliceBasedMetadata {
  static constexpr bool kRepeatable = false;
  using CompressionTraits = NoCompressionCompressor;
  static absl::string_view key() { return "lb-token"; }
};

// lb-cost-bin metadata
struct LbCostBinMetadata {
  static constexpr bool kRepeatable = true;
  static absl::string_view key() { return "lb-cost-bin"; }
  struct ValueType {
    double cost;
    std::string name;
  };
  using MementoType = ValueType;
  using CompressionTraits = NoCompressionCompressor;
  static ValueType MementoToValue(MementoType value) { return value; }
  static Slice Encode(const ValueType& x);
  static std::string DisplayValue(ValueType x);
  static std::string DisplayMemento(MementoType x) { return DisplayValue(x); }
  static MementoType ParseMemento(Slice value,
                                  bool will_keep_past_request_lifetime,
                                  MetadataParseErrorFn on_error);
};

// Annotation added by a transport to note whether a failed request was never
// placed on the wire, or never seen by a server.
struct GrpcStreamNetworkState {
  static absl::string_view DebugKey() { return "GrpcStreamNetworkState"; }
  static constexpr bool kRepeatable = false;
  enum ValueType : uint8_t {
    kNotSentOnWire,
    kNotSeenByServer,
  };
  static std::string DisplayValue(ValueType x);
};

// Annotation added by a server transport to note the peer making a request.
struct PeerString {
  static absl::string_view DebugKey() { return "PeerString"; }
  static constexpr bool kRepeatable = false;
  using ValueType = Slice;
  static std::string DisplayValue(const ValueType& x);
};

// Annotation added by various systems to describe the reason for a failure.
struct GrpcStatusContext {
  static absl::string_view DebugKey() { return "GrpcStatusContext"; }
  static constexpr bool kRepeatable = true;
  using ValueType = std::string;
  static const std::string& DisplayValue(const std::string& x);
};

// Annotation added by a transport to note that the status came from the wire.
struct GrpcStatusFromWire {
  static absl::string_view DebugKey() { return "GrpcStatusFromWire"; }
  static constexpr bool kRepeatable = false;
  using ValueType = bool;
  static absl::string_view DisplayValue(bool x) { return x ? "true" : "false"; }
};

// Annotation to denote that this call qualifies for cancelled=1 for the
// RECV_CLOSE_ON_SERVER op
struct GrpcCallWasCancelled {
  static absl::string_view DebugKey() { return "GrpcCallWasCancelled"; }
  static constexpr bool kRepeatable = false;
  using ValueType = bool;
  static absl::string_view DisplayValue(bool x) { return x ? "true" : "false"; }
};

// Annotation added by client surface code to denote wait-for-ready state
struct WaitForReady {
  struct ValueType {
    bool value = false;
    bool explicitly_set = false;
  };
  static absl::string_view DebugKey() { return "WaitForReady"; }
  static constexpr bool kRepeatable = false;
  static std::string DisplayValue(ValueType x);
};

// Annotation added by a transport to note that server trailing metadata
// is a Trailers-Only response.
struct GrpcTrailersOnly {
  static absl::string_view DebugKey() { return "GrpcTrailersOnly"; }
  static constexpr bool kRepeatable = false;
  using ValueType = bool;
  static absl::string_view DisplayValue(bool x) { return x ? "true" : "false"; }
};

// On the client-side, the value is a uintptr_t with a value of 1 if the call
// has a registered/known method, or 0, if it's not known. On the server side,
// the value is a (ChannelRegisteredMethod*).
struct GrpcRegisteredMethod {
  static absl::string_view DebugKey() { return "GrpcRegisteredMethod"; }
  static constexpr bool kRepeatable = false;
  using ValueType = void*;
  static std::string DisplayValue(void* x);
};

// Annotation added by filters to inform the transport to tarpit this
// response: add some random delay to thwart certain kinds of attacks.
struct GrpcTarPit {
  static absl::string_view DebugKey() { return "GrpcTarPit"; }
  static constexpr bool kRepeatable = false;
  using ValueType = Empty;
  static absl::string_view DisplayValue(Empty) { return "tarpit"; }
};

namespace metadata_detail {

// Build a key/value formatted debug string.
// Output looks like 'key1: value1, key2: value2'
// The string is expected to be readable, but not necessarily parsable.
class DebugStringBuilder {
 public:
  // Add one key/value pair to the output.
  void Add(absl::string_view key, absl::string_view value);

  // Finalize the output and return the string.
  // Subsequent Add calls are UB.
  std::string TakeOutput() { return std::move(out_); }

 private:
  std::string out_;
};

// IsEncodable: Given a trait, determine if that trait is encodable, or is
// just a value attached to a MetadataMap. We use the presence of the key()
// static method to determine if a trait is encodable or not - encodable
// traits have string names, and non-encodable traits do not.
template <typename Trait, typename Ignored = void>
struct IsEncodableTrait {
  static const bool value = false;
};

template <typename Trait>
struct IsEncodableTrait<Trait, absl::void_t<decltype(Trait::key())>> {
  static const bool value = true;
};

template <typename MustBeVoid, typename... Traits>
struct EncodableTraits;

template <typename Trait, typename... Traits>
struct EncodableTraits<absl::enable_if_t<IsEncodableTrait<Trait>::value, void>,
                       Trait, Traits...> {
  using List =
      typename EncodableTraits<void,
                               Traits...>::List::template PushFront<Trait>;
};

template <typename Trait, typename... Traits>
struct EncodableTraits<absl::enable_if_t<!IsEncodableTrait<Trait>::value, void>,
                       Trait, Traits...> {
  using List = typename EncodableTraits<void, Traits...>::List;
};

template <>
struct EncodableTraits<void> {
  using List = Typelist<>;
};

template <typename Trait>
struct EncodableNameLookupKeyComparison {
  bool operator()(absl::string_view key) { return key == Trait::key(); }
};

template <typename Trait, typename Op>
struct EncodableNameLookupOnFound {
  auto operator()(Op* op) { return op->Found(Trait()); }
};

template <typename... Traits>
struct EncodableNameLookup {
  template <typename Op>
  static auto Lookup(absl::string_view key, Op* op) {
    return IfList(
        key, op, [key](Op* op) { return op->NotFound(key); },
        EncodableNameLookupKeyComparison<Traits>()...,
        EncodableNameLookupOnFound<Traits, Op>()...);
  }
};

template <typename... Traits>
using NameLookup = typename EncodableTraits<
    void, Traits...>::List::template Instantiate<EncodableNameLookup>;

// Helper to take a slice to a memento to a value.
// By splitting this part out we can scale code size as the number of
// (memento, value) types, rather than as the number of traits.
template <typename ParseMementoFn, typename MementoToValueFn>
struct ParseValue {
  template <ParseMementoFn parse_memento, MementoToValueFn memento_to_value>
  static GPR_ATTRIBUTE_NOINLINE auto Parse(Slice* value,
                                           MetadataParseErrorFn on_error)
      -> decltype(memento_to_value(parse_memento(std::move(*value), false,
                                                 on_error))) {
    return memento_to_value(parse_memento(std::move(*value), false, on_error));
  }
};

// This is an "Op" type for NameLookup.
// Used for MetadataMap::Parse, its Found/NotFound methods turn a slice into a
// ParsedMetadata object.
template <typename Container>
class ParseHelper {
 public:
  ParseHelper(Slice value, bool will_keep_past_request_lifetime,
              MetadataParseErrorFn on_error, size_t transport_size)
      : value_(std::move(value)),
        will_keep_past_request_lifetime_(will_keep_past_request_lifetime),
        on_error_(on_error),
        transport_size_(transport_size) {}

  template <typename Trait>
  GPR_ATTRIBUTE_NOINLINE ParsedMetadata<Container> Found(Trait trait) {
    return ParsedMetadata<Container>(
        trait,
        ParseValueToMemento<typename Trait::MementoType, Trait::ParseMemento>(),
        static_cast<uint32_t>(transport_size_));
  }

  GPR_ATTRIBUTE_NOINLINE ParsedMetadata<Container> NotFound(
      absl::string_view key) {
    return ParsedMetadata<Container>(
        typename ParsedMetadata<Container>::FromSlicePair{},
        Slice::FromCopiedString(key),
        will_keep_past_request_lifetime_ ? value_.TakeUniquelyOwned()
                                         : std::move(value_),
        transport_size_);
  }

 private:
  template <typename T, T (*parse_memento)(Slice, bool, MetadataParseErrorFn)>
  GPR_ATTRIBUTE_NOINLINE T ParseValueToMemento() {
    return parse_memento(std::move(value_), will_keep_past_request_lifetime_,
                         on_error_);
  }

  Slice value_;
  const bool will_keep_past_request_lifetime_;
  MetadataParseErrorFn on_error_;
  const size_t transport_size_;
};

// This is an "Op" type for NameLookup.
// Used for MetadataMap::Append, its Found/NotFound methods turn a slice into
// a value and add it to a container.
template <typename Container>
class AppendHelper {
 public:
  AppendHelper(Container* container, Slice value, MetadataParseErrorFn on_error)
      : container_(container), value_(std::move(value)), on_error_(on_error) {}

  template <typename Trait>
  GPR_ATTRIBUTE_NOINLINE void Found(Trait trait) {
    container_->Set(
        trait, ParseValue<decltype(Trait::ParseMemento),
                          decltype(Trait::MementoToValue)>::
                   template Parse<Trait::ParseMemento, Trait::MementoToValue>(
                       &value_, on_error_));
  }

  GPR_ATTRIBUTE_NOINLINE void NotFound(absl::string_view key) {
    container_->unknown_.Append(key, std::move(value_));
  }

 private:
  Container* const container_;
  Slice value_;
  MetadataParseErrorFn on_error_;
};

// This is an "Op" type for NameLookup.
// Used for MetadataMap::Remove, its Found/NotFound methods remove a key from
// the container.
template <typename Container>
class RemoveHelper {
 public:
  explicit RemoveHelper(Container* container) : container_(container) {}

  template <typename Trait>
  GPR_ATTRIBUTE_NOINLINE void Found(Trait trait) {
    container_->Remove(trait);
  }

  GPR_ATTRIBUTE_NOINLINE void NotFound(absl::string_view key) {
    container_->unknown_.Remove(key);
  }

 private:
  Container* const container_;
};

// This is an "Op" type for NameLookup.
// Used for MetadataMap::GetStringValue, its Found/NotFound methods generated
// a string value from the container.
template <typename Container>
class GetStringValueHelper {
 public:
  explicit GetStringValueHelper(const Container* container,
                                std::string* backing)
      : container_(container), backing_(backing) {}

  template <typename Trait>
  GPR_ATTRIBUTE_NOINLINE absl::enable_if_t<
      Trait::kRepeatable == false &&
          std::is_same<Slice, typename Trait::ValueType>::value,
      absl::optional<absl::string_view>>
  Found(Trait) {
    const auto* value = container_->get_pointer(Trait());
    if (value == nullptr) return absl::nullopt;
    return value->as_string_view();
  }

  template <typename Trait>
  GPR_ATTRIBUTE_NOINLINE absl::enable_if_t<
      Trait::kRepeatable == true &&
          !std::is_same<Slice, typename Trait::ValueType>::value,
      absl::optional<absl::string_view>>
  Found(Trait) {
    const auto* value = container_->get_pointer(Trait());
    if (value == nullptr) return absl::nullopt;
    backing_->clear();
    for (const auto& v : *value) {
      if (!backing_->empty()) backing_->push_back(',');
      auto new_segment = Trait::Encode(v);
      backing_->append(new_segment.begin(), new_segment.end());
    }
    return *backing_;
  }

  template <typename Trait>
  GPR_ATTRIBUTE_NOINLINE absl::enable_if_t<
      Trait::kRepeatable == false &&
          !std::is_same<Slice, typename Trait::ValueType>::value,
      absl::optional<absl::string_view>>
  Found(Trait) {
    const auto* value = container_->get_pointer(Trait());
    if (value == nullptr) return absl::nullopt;
    *backing_ = std::string(Trait::Encode(*value).as_string_view());
    return *backing_;
  }

  GPR_ATTRIBUTE_NOINLINE absl::optional<absl::string_view> NotFound(
      absl::string_view key) {
    return container_->unknown_.GetStringValue(key, backing_);
  }

 private:
  const Container* const container_;
  std::string* backing_;
};

// Sink for key value logs
using LogFn = absl::FunctionRef<void(absl::string_view, absl::string_view)>;

template <typename T>
struct AdaptDisplayValueToLog {
  static std::string ToString(const T& value) { return std::to_string(value); }
};

template <>
struct AdaptDisplayValueToLog<std::string> {
  static std::string ToString(const std::string& value) { return value; }
};

template <>
struct AdaptDisplayValueToLog<const std::string&> {
  static std::string ToString(const std::string& value) { return value; }
};

template <>
struct AdaptDisplayValueToLog<absl::string_view> {
  static std::string ToString(absl::string_view value) {
    return std::string(value);
  }
};

template <>
struct AdaptDisplayValueToLog<Slice> {
  static std::string ToString(Slice value) {
    return std::string(value.as_string_view());
  }
};

template <>
struct AdaptDisplayValueToLog<const char*> {
  static std::string ToString(const char* value) { return std::string(value); }
};

template <>
struct AdaptDisplayValueToLog<StaticSlice> {
  static absl::string_view ToString(StaticSlice value) {
    return value.as_string_view();
  }
};

template <typename T, typename U, typename V>
GPR_ATTRIBUTE_NOINLINE void LogKeyValueTo(absl::string_view key, const T& value,
                                          V (*display_value)(U), LogFn log_fn) {
  log_fn(key, AdaptDisplayValueToLog<V>::ToString(display_value(value)));
}

// Generate a strong type for metadata values per trait.
template <typename Which, typename Ignored = void>
struct Value;

template <typename Which>
struct Value<Which, absl::enable_if_t<Which::kRepeatable == false &&
                                          IsEncodableTrait<Which>::value,
                                      void>> {
  Value() = default;
  explicit Value(const typename Which::ValueType& value) : value(value) {}
  explicit Value(typename Which::ValueType&& value)
      : value(std::forward<typename Which::ValueType>(value)) {}
  Value(const Value&) = delete;
  Value& operator=(const Value&) = delete;
  Value(Value&&) noexcept = default;
  Value& operator=(Value&& other) noexcept {
    value = std::move(other.value);
    return *this;
  }
  template <typename Encoder>
  void EncodeTo(Encoder* encoder) const {
    encoder->Encode(Which(), value);
  }
  template <typename Encoder>
  void VisitWith(Encoder* encoder) const {
    return EncodeTo(encoder);
  }
  void LogTo(LogFn log_fn) const {
    LogKeyValueTo(Which::key(), value, Which::DisplayValue, log_fn);
  }
  using StorageType = typename Which::ValueType;
  GPR_NO_UNIQUE_ADDRESS StorageType value;
};

template <typename Which>
struct Value<Which, absl::enable_if_t<Which::kRepeatable == false &&
                                          !IsEncodableTrait<Which>::value,
                                      void>> {
  Value() = default;
  explicit Value(const typename Which::ValueType& value) : value(value) {}
  explicit Value(typename Which::ValueType&& value)
      : value(std::forward<typename Which::ValueType>(value)) {}
  Value(const Value&) = delete;
  Value& operator=(const Value&) = delete;
  Value(Value&&) noexcept = default;
  Value& operator=(Value&& other) noexcept {
    value = std::move(other.value);
    return *this;
  }
  template <typename Encoder>
  void EncodeTo(Encoder*) const {}
  template <typename Encoder>
  void VisitWith(Encoder* encoder) const {
    encoder->Encode(Which(), value);
  }
  void LogTo(LogFn log_fn) const {
    LogKeyValueTo(Which::DebugKey(), value, Which::DisplayValue, log_fn);
  }
  using StorageType = typename Which::ValueType;
  GPR_NO_UNIQUE_ADDRESS StorageType value;
};

template <typename Which>
struct Value<Which, absl::enable_if_t<Which::kRepeatable == true &&
                                          IsEncodableTrait<Which>::value,
                                      void>> {
  Value() = default;
  explicit Value(const typename Which::ValueType& value) {
    this->value.push_back(value);
  }
  explicit Value(typename Which::ValueType&& value) {
    this->value.emplace_back(std::forward<typename Which::ValueType>(value));
  }
  Value(const Value&) = delete;
  Value& operator=(const Value&) = delete;
  Value(Value&& other) noexcept : value(std::move(other.value)) {}
  Value& operator=(Value&& other) noexcept {
    value = std::move(other.value);
    return *this;
  }
  template <typename Encoder>
  void EncodeTo(Encoder* encoder) const {
    for (const auto& v : value) {
      encoder->Encode(Which(), v);
    }
  }
  template <typename Encoder>
  void VisitWith(Encoder* encoder) const {
    return EncodeTo(encoder);
  }
  void LogTo(LogFn log_fn) const {
    for (const auto& v : value) {
      LogKeyValueTo(Which::key(), v, Which::Encode, log_fn);
    }
  }
  using StorageType = absl::InlinedVector<typename Which::ValueType, 1>;
  StorageType value;
};

template <typename Which>
struct Value<Which, absl::enable_if_t<Which::kRepeatable == true &&
                                          !IsEncodableTrait<Which>::value,
                                      void>> {
  Value() = default;
  explicit Value(const typename Which::ValueType& value) {
    this->value.push_back(value);
  }
  explicit Value(typename Which::ValueType&& value) {
    this->value.emplace_back(std::forward<typename Which::ValueType>(value));
  }
  Value(const Value&) = delete;
  Value& operator=(const Value&) = delete;
  Value(Value&& other) noexcept : value(std::move(other.value)) {}
  Value& operator=(Value&& other) noexcept {
    value = std::move(other.value);
    return *this;
  }
  template <typename Encoder>
  void EncodeTo(Encoder*) const {}
  template <typename Encoder>
  void VisitWith(Encoder* encoder) const {
    for (const auto& v : value) {
      encoder->Encode(Which(), v);
    }
  }
  void LogTo(LogFn log_fn) const {
    for (const auto& v : value) {
      LogKeyValueTo(Which::DebugKey(), v, Which::DisplayValue, log_fn);
    }
  }
  using StorageType = absl::InlinedVector<typename Which::ValueType, 1>;
  StorageType value;
};

// Encoder to copy some metadata
template <typename Output>
class CopySink {
 public:
  explicit CopySink(Output* dst) : dst_(dst) {}

  template <class T, class V>
  void Encode(T trait, V value) {
    dst_->Set(trait, value);
  }

  template <class T>
  void Encode(T trait, const Slice& value) {
    dst_->Set(trait, std::move(value.AsOwned()));
  }

  void Encode(const Slice& key, const Slice& value) {
    dst_->unknown_.Append(key.as_string_view(), value.Ref());
  }

 private:
  Output* dst_;
};

// Callable for the ForEach in Encode() -- for each value, call the
// appropriate encoder method.
template <typename Encoder>
struct EncodeWrapper {
  Encoder* encoder;
  template <typename Which>
  void operator()(const Value<Which>& which) {
    which.EncodeTo(encoder);
  }
};

// Callable for the table ForEach in ForEach() -- for each value, call the
// appropriate visitor method.
template <typename Encoder>
struct ForEachWrapper {
  Encoder* encoder;
  template <typename Which>
  void operator()(const Value<Which>& which) {
    which.VisitWith(encoder);
  }
};

// Callable for the ForEach in Log()
struct LogWrapper {
  LogFn log_fn;
  template <typename Which>
  void operator()(const Value<Which>& which) {
    which.LogTo(log_fn);
  }
};

// Encoder to compute TransportSize
class TransportSizeEncoder {
 public:
  void Encode(const Slice& key, const Slice& value) {
    size_ += key.length() + value.length() + 32;
  }

  template <typename Which>
  void Encode(Which, const typename Which::ValueType& value) {
    Add(Which(), value);
  }

  void Encode(ContentTypeMetadata,
              const typename ContentTypeMetadata::ValueType& value) {
    if (value == ContentTypeMetadata::kInvalid) return;
    Add(ContentTypeMetadata(), value);
  }

  size_t size() const { return size_; }

 private:
  template <typename Which>
  void Add(Which, const typename Which::ValueType& value) {
    size_ += Which::key().length() + Which::Encode(value).length() + 32;
  }

  uint32_t size_ = 0;
};

// Handle unknown (non-trait-based) fields in the metadata map.
class UnknownMap {
 public:
  explicit UnknownMap(Arena* arena) : unknown_(arena) {}

  using BackingType = ChunkedVector<std::pair<Slice, Slice>, 10>;

  void Append(absl::string_view key, Slice value);
  void Remove(absl::string_view key);
  absl::optional<absl::string_view> GetStringValue(absl::string_view key,
                                                   std::string* backing) const;

  BackingType::ConstForwardIterator begin() const { return unknown_.cbegin(); }
  BackingType::ConstForwardIterator end() const { return unknown_.cend(); }

  bool empty() const { return unknown_.empty(); }
  size_t size() const { return unknown_.size(); }
  void Clear() { unknown_.Clear(); }
  Arena* arena() const { return unknown_.arena(); }

 private:
  // Backing store for added metadata.
  ChunkedVector<std::pair<Slice, Slice>, 10> unknown_;
};

// Given a factory template Factory, construct a type that derives from
// Factory<MetadataTrait, MetadataTrait::CompressionTraits> for all
// MetadataTraits. Useful for transports in defining the stateful parts of their
// compression algorithm.
template <template <typename, typename> class Factory,
          typename... MetadataTraits>
struct StatefulCompressor;

template <template <typename, typename> class Factory, typename MetadataTrait,
          bool kEncodable = IsEncodableTrait<MetadataTrait>::value>
struct SpecificStatefulCompressor;

template <template <typename, typename> class Factory, typename MetadataTrait>
struct SpecificStatefulCompressor<Factory, MetadataTrait, true>
    : public Factory<MetadataTrait, typename MetadataTrait::CompressionTraits> {
};

template <template <typename, typename> class Factory, typename MetadataTrait>
struct SpecificStatefulCompressor<Factory, MetadataTrait, false> {};

template <template <typename, typename> class Factory, typename MetadataTrait,
          typename... MetadataTraits>
struct StatefulCompressor<Factory, MetadataTrait, MetadataTraits...>
    : public SpecificStatefulCompressor<Factory, MetadataTrait>,
      public StatefulCompressor<Factory, MetadataTraits...> {};

template <template <typename, typename> class Factory>
struct StatefulCompressor<Factory> {};

}  // namespace metadata_detail

// Helper function for encoders
// Given a metadata trait, convert the value to a slice.
template <typename Which>
absl::enable_if_t<std::is_same<typename Which::ValueType, Slice>::value,
                  const Slice&>
MetadataValueAsSlice(const Slice& slice) {
  return slice;
}

template <typename Which>
absl::enable_if_t<!std::is_same<typename Which::ValueType, Slice>::value, Slice>
MetadataValueAsSlice(typename Which::ValueType value) {
  return Slice(Which::Encode(value));
}

// MetadataMap encodes the mapping of metadata keys to metadata values.
//
// MetadataMap takes a derived class and list of traits. Each of these trait
// objects defines one metadata field that is used by core, and so should have
// more specialized handling than just using the generic APIs.
//
// MetadataMap is the backing type for some derived type via the curiously
// recursive template pattern. This is because many types consumed by
// MetadataMap require the container type to operate on, and many of those
// types are instantiated one per trait. A naive implementation without the
// Derived type would, for traits A,B,C, then instantiate for some
// T<Container, Trait>:
//  - T<MetadataMap<A,B,C>, A>,
//  - T<MetadataMap<A,B,C>, B>,
//  - T<MetadataMap<A,B,C>, C>.
// Since these types ultimately need to be recorded in the .dynstr segment
// for dynamic linkers (if gRPC is linked as a static library) this would
// create O(N^2) bytes of symbols even in stripped libraries. To avoid this
// we use the derived type (e.g. grpc_metadata_batch right now) to capture
// the container type, and we would write T<grpc_metadata_batch, A>, etc...
// Note that now the container type uses a number of bytes that is independent
// of the number of traits, and so we return to a linear symbol table growth
// function.
//
// Each trait object has one of two possible signatures, depending on whether
// that traits field is encodable or not.
// Non-encodable traits are carried in a MetadataMap, but are never passed to
// the application nor serialized to wire.
//
// Encodable traits have the following signature:
// // Traits for the "grpc-xyz" metadata field:
// struct GrpcXyzMetadata {
//   // Can this metadata field be repeated?
//   static constexpr bool kRepeatable = ...;
//   // The type that's stored on MetadataBatch
//   using ValueType = ...;
//   // The type that's stored in compression/decompression tables
//   using MementoType = ...;
//   // The string key for this metadata type (for transports that require it)
//   static absl::string_view key() { return "grpc-xyz"; }
//   // Parse a memento from a slice
//   // Takes ownership of value
//   // If will_keep_past_request_lifetime is true, expect that the returned
//   // memento will be kept for a long time, and so try not to keep a ref to
//   // the input slice.
//   // Calls fn in the case of an error that should be reported to the user
//   static MementoType ParseMemento(
//       Slice value,
//       bool will_keep_past_request_lifetime,
//       MementoParseErrorFn fn) {
//   ...
//   }
//   // Convert a memento to a value
//   static ValueType MementoToValue(MementoType memento) { ... }
//   // Convert a value to its canonical text wire format (the format that
//   // ParseMemento will accept!)
//   static Slice Encode(const ValueType& value);
//   // Convert a value to something that can be passed to StrCat and
//   displayed
//   // for debugging
//   static SomeStrCatableType DisplayValue(ValueType value) { ... }
//   static SomeStrCatableType DisplayMemento(MementoType value) { ... }
// };
//
// Non-encodable traits are determined by missing the key() method, and have
// the following signature (and by convention omit the Metadata part of the
// type name):
// // Traits for the GrpcXyz field:
// struct GrpcXyz {
//   // The string key that should be used for debug dumps - should not be a
//   // valid http2 key (ie all lower case)
//   static absl::string_view DebugKey() { return "GRPC_XYZ"; }
//   // Can this metadata field be repeated?
//   static constexpr bool kRepeatable = ...;
//   // The type that's stored on MetadataBatch
//   using ValueType = ...;
//   // Convert a value to something that can be passed to StrCat and
//   displayed
//   // for debugging
//   static SomeStrCatableType DisplayValue(ValueType value) { ... }
// };
//
// About parsing and mementos:
//
// Many gRPC transports exchange metadata as key/value strings, but also allow
// for a more efficient representation as a single integer. We can use this
// integer representation to avoid reparsing too, by storing the parsed value
// in the compression table. This is what mementos are used for.
//
// A trait offers the capability to turn a slice into a memento via
// ParseMemento. This is exposed to users of MetadataMap via the Parse()
// method, that returns a ParsedMetadata object. That ParsedMetadata object
// can in turn be used to set the same value on many different MetadataMaps
// without having to reparse.
//
// Implementation wise, ParsedMetadata is a type erased wrapper around
// MementoType. When we set a value on MetadataMap, we first turn that memento
// into a value. For most types, this is going to be a no-op, but for example
// for grpc-timeout we make the memento the timeout expressed on the wire, but
// we make the value the timestamp of when the timeout will expire (i.e. the
// deadline).
template <class Derived, typename... Traits>
class MetadataMap {
 public:
  explicit MetadataMap(Arena* arena);
  ~MetadataMap();

  // Given a compressor factory - template taking <MetadataTrait,
  // CompressionTrait>, StatefulCompressor<Factory> provides a type
  // derived from all Encodable traits in this MetadataMap.
  // This can be used by transports to delegate compression to the appropriate
  // compression algorithm.
  template <template <typename, typename> class Factory>
  using StatefulCompressor =
      metadata_detail::StatefulCompressor<Factory, Traits...>;

  MetadataMap(const MetadataMap&) = delete;
  MetadataMap& operator=(const MetadataMap&) = delete;
  MetadataMap(MetadataMap&&) noexcept;
  // We never create MetadataMap directly, instead we create Derived, but we
  // want to be able to move it without redeclaring this.
  // NOLINTNEXTLINE(misc-unconventional-assign-operator)
  Derived& operator=(MetadataMap&&) noexcept;

  // Encode this metadata map into some encoder.
  // For each field that is set in the MetadataMap, call
  // encoder->Encode.
  //
  // For fields for which we have traits, this will be a method with
  // the signature:
  //    void Encode(TraitsType, typename TraitsType::ValueType value);
  // For fields for which we do not have traits, this will be a method
  // with the signature:
  //    void Encode(string_view key, Slice value);
  template <typename Encoder>
  void Encode(Encoder* encoder) const {
    table_.template ForEachIn<metadata_detail::EncodeWrapper<Encoder>,
                              Value<Traits>...>(
        metadata_detail::EncodeWrapper<Encoder>{encoder});
    for (const auto& unk : unknown_) {
      encoder->Encode(unk.first, unk.second);
    }
  }

  // Like Encode, but also visit the non-encodable fields.
  template <typename Encoder>
  void ForEach(Encoder* encoder) const {
    table_.ForEach(metadata_detail::ForEachWrapper<Encoder>{encoder});
    for (const auto& unk : unknown_) {
      encoder->Encode(unk.first, unk.second);
    }
  }

  // Similar to Encode, but targeted at logging: for each metadatum,
  // call f(key, value) as absl::string_views.
  void Log(metadata_detail::LogFn log_fn) const {
    table_.ForEach(metadata_detail::LogWrapper{log_fn});
    for (const auto& unk : unknown_) {
      log_fn(unk.first.as_string_view(), unk.second.as_string_view());
    }
  }

  std::string DebugString() const {
    metadata_detail::DebugStringBuilder builder;
    Log([&builder](absl::string_view key, absl::string_view value) {
      builder.Add(key, value);
    });
    return builder.TakeOutput();
  }

  // Get the pointer to the value of some known metadata.
  // Returns nullptr if the metadata is not present.
  // Causes a compilation error if Which is not an element of Traits.
  template <typename Which>
  const typename metadata_detail::Value<Which>::StorageType* get_pointer(
      Which) const {
    if (auto* p = table_.template get<Value<Which>>()) return &p->value;
    return nullptr;
  }

  // Get the pointer to the value of some known metadata.
  // Returns nullptr if the metadata is not present.
  // Causes a compilation error if Which is not an element of Traits.
  template <typename Which>
  typename metadata_detail::Value<Which>::StorageType* get_pointer(Which) {
    if (auto* p = table_.template get<Value<Which>>()) return &p->value;
    return nullptr;
  }

  // Get the pointer to the value of some known metadata.
  // Adds the default value for the metadata is not present.
  // Causes a compilation error if Which is not an element of Traits.
  template <typename Which>
  typename metadata_detail::Value<Which>::StorageType* GetOrCreatePointer(
      Which) {
    return &table_.template get_or_create<Value<Which>>()->value;
  }

  // Get the value of some known metadata.
  // Returns nullopt if the metadata is not present.
  // Causes a compilation error if Which is not an element of Traits.
  template <typename Which>
  absl::optional<typename Which::ValueType> get(Which) const {
    if (auto* p = table_.template get<Value<Which>>()) return p->value;
    return absl::nullopt;
  }

  // Set the value of some known metadata.
  // Returns a pointer to the new value.
  template <typename Which, typename... Args>
  absl::enable_if_t<Which::kRepeatable == false, void> Set(Which,
                                                           Args&&... args) {
    table_.template set<Value<Which>>(std::forward<Args>(args)...);
  }
  template <typename Which, typename... Args>
  absl::enable_if_t<Which::kRepeatable == true, void> Set(Which,
                                                          Args&&... args) {
    GetOrCreatePointer(Which())->emplace_back(std::forward<Args>(args)...);
  }

  // Remove a specific piece of known metadata.
  template <typename Which>
  void Remove(Which) {
    table_.template clear<Value<Which>>();
  }

  // Remove some metadata by name
  void Remove(absl::string_view key) {
    metadata_detail::RemoveHelper<Derived> helper(static_cast<Derived*>(this));
    metadata_detail::NameLookup<Traits...>::Lookup(key, &helper);
  }

  void Remove(const char* key) { Remove(absl::string_view(key)); }

  // Retrieve some metadata by name
  absl::optional<absl::string_view> GetStringValue(absl::string_view name,
                                                   std::string* buffer) const {
    metadata_detail::GetStringValueHelper<Derived> helper(
        static_cast<const Derived*>(this), buffer);
    return metadata_detail::NameLookup<Traits...>::Lookup(name, &helper);
  }

  // Extract a piece of known metadata.
  // Returns nullopt if the metadata was not present, or the value if it was.
  // The same as:
  //  auto value = m.get(T());
  //  m.Remove(T());
  template <typename Which>
  absl::enable_if_t<Which::kRepeatable == false,
                    absl::optional<typename Which::ValueType>>
  Take(Which which) {
    if (auto* p = get_pointer(which)) {
      absl::optional<typename Which::ValueType> value(std::move(*p));
      Remove(which);
      return value;
    }
    return {};
  }

  // Extract repeated known metadata.
  // Returns an empty vector if the metadata was not present.
  template <typename Which>
  absl::enable_if_t<Which::kRepeatable == true,
                    typename metadata_detail::Value<Which>::StorageType>
  Take(Which which) {
    if (auto* p = get_pointer(which)) {
      typename Value<Which>::StorageType value = std::move(*p);
      Remove(which);
      return value;
    }
    return {};
  }

  // Parse metadata from a key/value pair, and return an object representing
  // that result.
  static ParsedMetadata<Derived> Parse(absl::string_view key, Slice value,
                                       bool will_keep_past_request_lifetime,
                                       uint32_t transport_size,
                                       MetadataParseErrorFn on_error) {
    metadata_detail::ParseHelper<Derived> helper(
        value.TakeOwned(), will_keep_past_request_lifetime, on_error,
        transport_size);
    return metadata_detail::NameLookup<Traits...>::Lookup(key, &helper);
  }

  // Set a value from a parsed metadata object.
  void Set(const ParsedMetadata<Derived>& m) {
    m.SetOnContainer(static_cast<Derived*>(this));
  }

  // Append a key/value pair - takes ownership of value
  void Append(absl::string_view key, Slice value,
              MetadataParseErrorFn on_error) {
    metadata_detail::AppendHelper<Derived> helper(static_cast<Derived*>(this),
                                                  value.TakeOwned(), on_error);
    metadata_detail::NameLookup<Traits...>::Lookup(key, &helper);
  }

  void Clear();
  size_t TransportSize() const;
  Derived Copy() const;
  bool empty() const { return table_.empty() && unknown_.empty(); }
  size_t count() const { return table_.count() + unknown_.size(); }

 private:
  friend class metadata_detail::AppendHelper<Derived>;
  friend class metadata_detail::GetStringValueHelper<Derived>;
  friend class metadata_detail::RemoveHelper<Derived>;
  friend class metadata_detail::CopySink<Derived>;
  friend class ParsedMetadata<Derived>;

  template <typename Which>
  using Value = metadata_detail::Value<Which>;

  // Table of known metadata types.
  PackedTable<Value<Traits>...> table_;
  metadata_detail::UnknownMap unknown_;
};

// Ok/not-ok check for metadata maps that contain GrpcStatusMetadata, so that
// they can be used as result types for TrySeq.
template <typename Derived, typename... Args>
inline bool IsStatusOk(const MetadataMap<Derived, Args...>& m) {
  return m.get(GrpcStatusMetadata()).value_or(GRPC_STATUS_UNKNOWN) ==
         GRPC_STATUS_OK;
}

template <typename Derived, typename... Traits>
MetadataMap<Derived, Traits...>::MetadataMap(Arena* arena) : unknown_(arena) {}

template <typename Derived, typename... Traits>
MetadataMap<Derived, Traits...>::MetadataMap(MetadataMap&& other) noexcept
    : table_(std::move(other.table_)), unknown_(std::move(other.unknown_)) {}

// We never create MetadataMap directly, instead we create Derived, but we
// want to be able to move it without redeclaring this.
// NOLINTNEXTLINE(misc-unconventional-assign-operator)
template <typename Derived, typename... Traits>
Derived& MetadataMap<Derived, Traits...>::operator=(
    MetadataMap&& other) noexcept {
  table_ = std::move(other.table_);
  unknown_ = std::move(other.unknown_);
  return static_cast<Derived&>(*this);
}

template <typename Derived, typename... Traits>
MetadataMap<Derived, Traits...>::~MetadataMap() = default;

template <typename Derived, typename... Traits>
void MetadataMap<Derived, Traits...>::Clear() {
  table_.ClearAll();
  unknown_.Clear();
}

template <typename Derived, typename... Traits>
size_t MetadataMap<Derived, Traits...>::TransportSize() const {
  metadata_detail::TransportSizeEncoder enc;
  Encode(&enc);
  return enc.size();
}

template <typename Derived, typename... Traits>
Derived MetadataMap<Derived, Traits...>::Copy() const {
  Derived out(unknown_.arena());
  metadata_detail::CopySink<Derived> sink(&out);
  ForEach(&sink);
  return out;
}

}  // namespace grpc_core

struct grpc_metadata_batch;

using grpc_metadata_batch_base = grpc_core::MetadataMap<
    grpc_metadata_batch,
    // Colon prefixed headers first
    grpc_core::HttpPathMetadata, grpc_core::HttpAuthorityMetadata,
    grpc_core::HttpMethodMetadata, grpc_core::HttpStatusMetadata,
    grpc_core::HttpSchemeMetadata,
    // Non-colon prefixed headers begin here
    grpc_core::ContentTypeMetadata, grpc_core::TeMetadata,
    grpc_core::GrpcEncodingMetadata, grpc_core::GrpcInternalEncodingRequest,
    grpc_core::GrpcAcceptEncodingMetadata, grpc_core::GrpcStatusMetadata,
    grpc_core::GrpcTimeoutMetadata, grpc_core::GrpcPreviousRpcAttemptsMetadata,
    grpc_core::GrpcRetryPushbackMsMetadata, grpc_core::UserAgentMetadata,
    grpc_core::GrpcMessageMetadata, grpc_core::HostMetadata,
    grpc_core::EndpointLoadMetricsBinMetadata,
    grpc_core::GrpcServerStatsBinMetadata, grpc_core::GrpcTraceBinMetadata,
    grpc_core::GrpcTagsBinMetadata, grpc_core::GrpcLbClientStatsMetadata,
    grpc_core::LbCostBinMetadata, grpc_core::LbTokenMetadata,
    grpc_core::XEnvoyPeerMetadata,
    // Non-encodable things
    grpc_core::GrpcStreamNetworkState, grpc_core::PeerString,
    grpc_core::GrpcStatusContext, grpc_core::GrpcStatusFromWire,
    grpc_core::GrpcCallWasCancelled, grpc_core::WaitForReady,
    grpc_core::GrpcTrailersOnly, grpc_core::GrpcTarPit,
    grpc_core::GrpcRegisteredMethod GRPC_CUSTOM_CLIENT_METADATA
        GRPC_CUSTOM_SERVER_METADATA>;

struct grpc_metadata_batch : public grpc_metadata_batch_base {
  using grpc_metadata_batch_base::grpc_metadata_batch_base;
};

#endif  // GRPC_SRC_CORE_LIB_TRANSPORT_METADATA_BATCH_H