|  | @@ -88,7 +88,19 @@ typedef struct {
 | 
	
		
			
				|  |  |   * with a data frame header */
 | 
	
		
			
				|  |  |  static void fill_header(uint8_t* p, uint8_t type, uint32_t id, size_t len,
 | 
	
		
			
				|  |  |                          uint8_t flags) {
 | 
	
		
			
				|  |  | -  GPR_ASSERT(len < 16777316);
 | 
	
		
			
				|  |  | +  /* len is the current frame size (i.e. for the frame we're finishing).
 | 
	
		
			
				|  |  | +     We finish a frame if:
 | 
	
		
			
				|  |  | +     1) We called ensure_space(), (i.e. add_tiny_header_data()) and adding
 | 
	
		
			
				|  |  | +        'need_bytes' to the frame would cause us to exceed st->max_frame_size.
 | 
	
		
			
				|  |  | +     2) We called add_header_data, and adding the slice would cause us to exceed
 | 
	
		
			
				|  |  | +        st->max_frame_size.
 | 
	
		
			
				|  |  | +     3) We're done encoding the header.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +     Thus, len is always <= st->max_frame_size.
 | 
	
		
			
				|  |  | +     st->max_frame_size is derived from GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE,
 | 
	
		
			
				|  |  | +     which has a max allowable value of 16777215 (see chttp_transport.cc).
 | 
	
		
			
				|  |  | +     Thus, the following assert can be a debug assert. */
 | 
	
		
			
				|  |  | +  GPR_DEBUG_ASSERT(len < 16777316);
 | 
	
		
			
				|  |  |    *p++ = static_cast<uint8_t>(len >> 16);
 | 
	
		
			
				|  |  |    *p++ = static_cast<uint8_t>(len >> 8);
 | 
	
		
			
				|  |  |    *p++ = static_cast<uint8_t>(len);
 | 
	
	
		
			
				|  | @@ -100,6 +112,13 @@ static void fill_header(uint8_t* p, uint8_t type, uint32_t id, size_t len,
 | 
	
		
			
				|  |  |    *p++ = static_cast<uint8_t>(id);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static size_t current_frame_size(framer_state* st) {
 | 
	
		
			
				|  |  | +  const size_t frame_size =
 | 
	
		
			
				|  |  | +      st->output->length - st->output_length_at_start_of_frame;
 | 
	
		
			
				|  |  | +  GPR_DEBUG_ASSERT(frame_size <= st->max_frame_size);
 | 
	
		
			
				|  |  | +  return frame_size;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /* finish a frame - fill in the previously reserved header */
 | 
	
		
			
				|  |  |  static void finish_frame(framer_state* st, int is_header_boundary,
 | 
	
		
			
				|  |  |                           int is_last_in_stream) {
 | 
	
	
		
			
				|  | @@ -108,7 +127,7 @@ static void finish_frame(framer_state* st, int is_header_boundary,
 | 
	
		
			
				|  |  |                              : GRPC_CHTTP2_FRAME_CONTINUATION;
 | 
	
		
			
				|  |  |    fill_header(
 | 
	
		
			
				|  |  |        GRPC_SLICE_START_PTR(st->output->slices[st->header_idx]), type,
 | 
	
		
			
				|  |  | -      st->stream_id, st->output->length - st->output_length_at_start_of_frame,
 | 
	
		
			
				|  |  | +      st->stream_id, current_frame_size(st),
 | 
	
		
			
				|  |  |        static_cast<uint8_t>(
 | 
	
		
			
				|  |  |            (is_last_in_stream ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0) |
 | 
	
		
			
				|  |  |            (is_header_boundary ? GRPC_CHTTP2_DATA_FLAG_END_HEADERS : 0)));
 | 
	
	
		
			
				|  | @@ -130,9 +149,7 @@ static void begin_frame(framer_state* st) {
 | 
	
		
			
				|  |  |     space to add at least about_to_add bytes -- finishes the current frame if
 | 
	
		
			
				|  |  |     needed */
 | 
	
		
			
				|  |  |  static void ensure_space(framer_state* st, size_t need_bytes) {
 | 
	
		
			
				|  |  | -  if (GPR_LIKELY(st->output->length - st->output_length_at_start_of_frame +
 | 
	
		
			
				|  |  | -                     need_bytes <=
 | 
	
		
			
				|  |  | -                 st->max_frame_size)) {
 | 
	
		
			
				|  |  | +  if (GPR_LIKELY(current_frame_size(st) + need_bytes <= st->max_frame_size)) {
 | 
	
		
			
				|  |  |      return;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    finish_frame(st, 0, 0);
 | 
	
	
		
			
				|  | @@ -158,8 +175,7 @@ static void add_header_data(framer_state* st, grpc_slice slice) {
 | 
	
		
			
				|  |  |    size_t len = GRPC_SLICE_LENGTH(slice);
 | 
	
		
			
				|  |  |    size_t remaining;
 | 
	
		
			
				|  |  |    if (len == 0) return;
 | 
	
		
			
				|  |  | -  remaining = st->max_frame_size + st->output_length_at_start_of_frame -
 | 
	
		
			
				|  |  | -              st->output->length;
 | 
	
		
			
				|  |  | +  remaining = st->max_frame_size - current_frame_size(st);
 | 
	
		
			
				|  |  |    if (len <= remaining) {
 | 
	
		
			
				|  |  |      st->stats->header_bytes += len;
 | 
	
		
			
				|  |  |      grpc_slice_buffer_add(st->output, slice);
 | 
	
	
		
			
				|  | @@ -325,132 +341,129 @@ static void emit_indexed(grpc_chttp2_hpack_compressor* c, uint32_t elem_index,
 | 
	
		
			
				|  |  |                             len);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -typedef struct {
 | 
	
		
			
				|  |  | -  grpc_slice data;
 | 
	
		
			
				|  |  | -  uint8_t huffman_prefix;
 | 
	
		
			
				|  |  | -  bool insert_null_before_wire_value;
 | 
	
		
			
				|  |  | -} wire_value;
 | 
	
		
			
				|  |  | +struct wire_value {
 | 
	
		
			
				|  |  | +  wire_value(uint8_t huffman_prefix, bool insert_null_before_wire_value,
 | 
	
		
			
				|  |  | +             const grpc_slice& slice)
 | 
	
		
			
				|  |  | +      : data(slice),
 | 
	
		
			
				|  |  | +        huffman_prefix(huffman_prefix),
 | 
	
		
			
				|  |  | +        insert_null_before_wire_value(insert_null_before_wire_value),
 | 
	
		
			
				|  |  | +        length(GRPC_SLICE_LENGTH(slice) +
 | 
	
		
			
				|  |  | +               (insert_null_before_wire_value ? 1 : 0)) {}
 | 
	
		
			
				|  |  | +  // While wire_value is const from the POV of hpack encoder code, actually
 | 
	
		
			
				|  |  | +  // adding it to a slice buffer will possibly split the slice.
 | 
	
		
			
				|  |  | +  const grpc_slice data;
 | 
	
		
			
				|  |  | +  const uint8_t huffman_prefix;
 | 
	
		
			
				|  |  | +  const bool insert_null_before_wire_value;
 | 
	
		
			
				|  |  | +  const size_t length;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  template <bool mdkey_definitely_interned>
 | 
	
		
			
				|  |  |  static wire_value get_wire_value(grpc_mdelem elem, bool true_binary_enabled) {
 | 
	
		
			
				|  |  | -  wire_value wire_val;
 | 
	
		
			
				|  |  | -  bool is_bin_hdr =
 | 
	
		
			
				|  |  | +  const bool is_bin_hdr =
 | 
	
		
			
				|  |  |        mdkey_definitely_interned
 | 
	
		
			
				|  |  |            ? grpc_is_refcounted_slice_binary_header(GRPC_MDKEY(elem))
 | 
	
		
			
				|  |  |            : grpc_is_binary_header_internal(GRPC_MDKEY(elem));
 | 
	
		
			
				|  |  | +  const grpc_slice& value = GRPC_MDVALUE(elem);
 | 
	
		
			
				|  |  |    if (is_bin_hdr) {
 | 
	
		
			
				|  |  |      if (true_binary_enabled) {
 | 
	
		
			
				|  |  |        GRPC_STATS_INC_HPACK_SEND_BINARY();
 | 
	
		
			
				|  |  | -      wire_val.huffman_prefix = 0x00;
 | 
	
		
			
				|  |  | -      wire_val.insert_null_before_wire_value = true;
 | 
	
		
			
				|  |  | -      wire_val.data = grpc_slice_ref_internal(GRPC_MDVALUE(elem));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +      return wire_value(0x00, true, grpc_slice_ref_internal(value));
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  |        GRPC_STATS_INC_HPACK_SEND_BINARY_BASE64();
 | 
	
		
			
				|  |  | -      wire_val.huffman_prefix = 0x80;
 | 
	
		
			
				|  |  | -      wire_val.insert_null_before_wire_value = false;
 | 
	
		
			
				|  |  | -      wire_val.data =
 | 
	
		
			
				|  |  | -          grpc_chttp2_base64_encode_and_huffman_compress(GRPC_MDVALUE(elem));
 | 
	
		
			
				|  |  | +      return wire_value(0x80, false,
 | 
	
		
			
				|  |  | +                        grpc_chttp2_base64_encode_and_huffman_compress(value));
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    } else {
 | 
	
		
			
				|  |  |      /* TODO(ctiller): opportunistically compress non-binary headers */
 | 
	
		
			
				|  |  |      GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED();
 | 
	
		
			
				|  |  | -    wire_val.huffman_prefix = 0x00;
 | 
	
		
			
				|  |  | -    wire_val.insert_null_before_wire_value = false;
 | 
	
		
			
				|  |  | -    wire_val.data = grpc_slice_ref_internal(GRPC_MDVALUE(elem));
 | 
	
		
			
				|  |  | +    return wire_value(0x00, false, grpc_slice_ref_internal(value));
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  return wire_val;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -static size_t wire_value_length(wire_value v) {
 | 
	
		
			
				|  |  | -  return GPR_SLICE_LENGTH(v.data) + v.insert_null_before_wire_value;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void add_wire_value(framer_state* st, wire_value v) {
 | 
	
		
			
				|  |  | -  if (v.insert_null_before_wire_value) *add_tiny_header_data(st, 1) = 0;
 | 
	
		
			
				|  |  | -  add_header_data(st, v.data);
 | 
	
		
			
				|  |  | +static uint32_t wire_value_length(const wire_value& v) {
 | 
	
		
			
				|  |  | +  GPR_DEBUG_ASSERT(v.length <= UINT32_MAX);
 | 
	
		
			
				|  |  | +  return static_cast<uint32_t>(v.length);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void emit_lithdr_incidx(grpc_chttp2_hpack_compressor* c,
 | 
	
		
			
				|  |  | -                               uint32_t key_index, grpc_mdelem elem,
 | 
	
		
			
				|  |  | -                               framer_state* st) {
 | 
	
		
			
				|  |  | -  GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX();
 | 
	
		
			
				|  |  | -  uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 2);
 | 
	
		
			
				|  |  | -  wire_value value = get_wire_value<true>(elem, st->use_true_binary_metadata);
 | 
	
		
			
				|  |  | -  size_t len_val = wire_value_length(value);
 | 
	
		
			
				|  |  | -  uint32_t len_val_len;
 | 
	
		
			
				|  |  | -  GPR_ASSERT(len_val <= UINT32_MAX);
 | 
	
		
			
				|  |  | -  len_val_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)len_val, 1);
 | 
	
		
			
				|  |  | -  GPR_DEBUG_ASSERT(len_pfx + len_val_len < GRPC_SLICE_INLINED_SIZE);
 | 
	
		
			
				|  |  | -  uint8_t* data = add_tiny_header_data(st, len_pfx + len_val_len);
 | 
	
		
			
				|  |  | -  GRPC_CHTTP2_WRITE_VARINT(key_index, 2, 0x40, data, len_pfx);
 | 
	
		
			
				|  |  | -  GRPC_CHTTP2_WRITE_VARINT((uint32_t)len_val, 1, value.huffman_prefix,
 | 
	
		
			
				|  |  | -                           &data[len_pfx], len_val_len);
 | 
	
		
			
				|  |  | -  add_wire_value(st, value);
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -static void emit_lithdr_noidx(grpc_chttp2_hpack_compressor* c,
 | 
	
		
			
				|  |  | -                              uint32_t key_index, grpc_mdelem elem,
 | 
	
		
			
				|  |  | -                              framer_state* st) {
 | 
	
		
			
				|  |  | -  GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX();
 | 
	
		
			
				|  |  | -  uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 4);
 | 
	
		
			
				|  |  | -  wire_value value = get_wire_value<false>(elem, st->use_true_binary_metadata);
 | 
	
		
			
				|  |  | -  size_t len_val = wire_value_length(value);
 | 
	
		
			
				|  |  | -  uint32_t len_val_len;
 | 
	
		
			
				|  |  | -  GPR_ASSERT(len_val <= UINT32_MAX);
 | 
	
		
			
				|  |  | -  len_val_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)len_val, 1);
 | 
	
		
			
				|  |  | +namespace {
 | 
	
		
			
				|  |  | +enum class EmitLitHdrType { INC_IDX, NO_IDX };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +enum class EmitLitHdrVType { INC_IDX_V, NO_IDX_V };
 | 
	
		
			
				|  |  | +}  // namespace
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +template <EmitLitHdrType type>
 | 
	
		
			
				|  |  | +static void emit_lithdr(grpc_chttp2_hpack_compressor* c, uint32_t key_index,
 | 
	
		
			
				|  |  | +                        grpc_mdelem elem, framer_state* st) {
 | 
	
		
			
				|  |  | +  switch (type) {
 | 
	
		
			
				|  |  | +    case EmitLitHdrType::INC_IDX:
 | 
	
		
			
				|  |  | +      GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX();
 | 
	
		
			
				|  |  | +      break;
 | 
	
		
			
				|  |  | +    case EmitLitHdrType::NO_IDX:
 | 
	
		
			
				|  |  | +      GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX();
 | 
	
		
			
				|  |  | +      break;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  const uint32_t len_pfx = type == EmitLitHdrType::INC_IDX
 | 
	
		
			
				|  |  | +                               ? GRPC_CHTTP2_VARINT_LENGTH(key_index, 2)
 | 
	
		
			
				|  |  | +                               : GRPC_CHTTP2_VARINT_LENGTH(key_index, 4);
 | 
	
		
			
				|  |  | +  const wire_value value =
 | 
	
		
			
				|  |  | +      get_wire_value<true>(elem, st->use_true_binary_metadata);
 | 
	
		
			
				|  |  | +  const uint32_t len_val = wire_value_length(value);
 | 
	
		
			
				|  |  | +  const uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1);
 | 
	
		
			
				|  |  |    GPR_DEBUG_ASSERT(len_pfx + len_val_len < GRPC_SLICE_INLINED_SIZE);
 | 
	
		
			
				|  |  | -  uint8_t* data = add_tiny_header_data(st, len_pfx + len_val_len);
 | 
	
		
			
				|  |  | -  GRPC_CHTTP2_WRITE_VARINT(key_index, 4, 0x00, data, len_pfx);
 | 
	
		
			
				|  |  | -  GRPC_CHTTP2_WRITE_VARINT((uint32_t)len_val, 1, value.huffman_prefix,
 | 
	
		
			
				|  |  | -                           &data[len_pfx], len_val_len);
 | 
	
		
			
				|  |  | -  add_wire_value(st, value);
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -static void emit_lithdr_incidx_v(grpc_chttp2_hpack_compressor* c,
 | 
	
		
			
				|  |  | -                                 uint32_t unused_index, grpc_mdelem elem,
 | 
	
		
			
				|  |  | -                                 framer_state* st) {
 | 
	
		
			
				|  |  | -  GPR_ASSERT(unused_index == 0);
 | 
	
		
			
				|  |  | -  GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX_V();
 | 
	
		
			
				|  |  | -  GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED();
 | 
	
		
			
				|  |  | -  uint32_t len_key = static_cast<uint32_t> GRPC_SLICE_LENGTH(GRPC_MDKEY(elem));
 | 
	
		
			
				|  |  | -  wire_value value = get_wire_value<true>(elem, st->use_true_binary_metadata);
 | 
	
		
			
				|  |  | -  uint32_t len_val = static_cast<uint32_t>(wire_value_length(value));
 | 
	
		
			
				|  |  | -  uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1);
 | 
	
		
			
				|  |  | -  uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1);
 | 
	
		
			
				|  |  | -  GPR_ASSERT(len_key <= UINT32_MAX);
 | 
	
		
			
				|  |  | -  GPR_ASSERT(wire_value_length(value) <= UINT32_MAX);
 | 
	
		
			
				|  |  | -  GPR_DEBUG_ASSERT(1 + len_key_len < GRPC_SLICE_INLINED_SIZE);
 | 
	
		
			
				|  |  | -  uint8_t* data = add_tiny_header_data(st, 1 + len_key_len);
 | 
	
		
			
				|  |  | -  data[0] = 0x40;
 | 
	
		
			
				|  |  | -  GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, &data[1], len_key_len);
 | 
	
		
			
				|  |  | -  add_header_data(st, grpc_slice_ref_internal(GRPC_MDKEY(elem)));
 | 
	
		
			
				|  |  | -  GRPC_CHTTP2_WRITE_VARINT(len_val, 1, value.huffman_prefix,
 | 
	
		
			
				|  |  | -                           add_tiny_header_data(st, len_val_len), len_val_len);
 | 
	
		
			
				|  |  | -  add_wire_value(st, value);
 | 
	
		
			
				|  |  | +  uint8_t* data = add_tiny_header_data(
 | 
	
		
			
				|  |  | +      st,
 | 
	
		
			
				|  |  | +      len_pfx + len_val_len + (value.insert_null_before_wire_value ? 1 : 0));
 | 
	
		
			
				|  |  | +  switch (type) {
 | 
	
		
			
				|  |  | +    case EmitLitHdrType::INC_IDX:
 | 
	
		
			
				|  |  | +      GRPC_CHTTP2_WRITE_VARINT(key_index, 2, 0x40, data, len_pfx);
 | 
	
		
			
				|  |  | +      break;
 | 
	
		
			
				|  |  | +    case EmitLitHdrType::NO_IDX:
 | 
	
		
			
				|  |  | +      GRPC_CHTTP2_WRITE_VARINT(key_index, 4, 0x00, data, len_pfx);
 | 
	
		
			
				|  |  | +      break;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  GRPC_CHTTP2_WRITE_VARINT(len_val, 1, value.huffman_prefix, &data[len_pfx],
 | 
	
		
			
				|  |  | +                           len_val_len);
 | 
	
		
			
				|  |  | +  if (value.insert_null_before_wire_value) {
 | 
	
		
			
				|  |  | +    data[len_pfx + len_val_len] = 0;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  add_header_data(st, value.data);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static void emit_lithdr_noidx_v(grpc_chttp2_hpack_compressor* c,
 | 
	
		
			
				|  |  | -                                uint32_t unused_index, grpc_mdelem elem,
 | 
	
		
			
				|  |  | -                                framer_state* st) {
 | 
	
		
			
				|  |  | -  GPR_ASSERT(unused_index == 0);
 | 
	
		
			
				|  |  | -  GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX_V();
 | 
	
		
			
				|  |  | +template <EmitLitHdrVType type>
 | 
	
		
			
				|  |  | +static void emit_lithdr_v(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem,
 | 
	
		
			
				|  |  | +                          framer_state* st) {
 | 
	
		
			
				|  |  | +  switch (type) {
 | 
	
		
			
				|  |  | +    case EmitLitHdrVType::INC_IDX_V:
 | 
	
		
			
				|  |  | +      GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX_V();
 | 
	
		
			
				|  |  | +      break;
 | 
	
		
			
				|  |  | +    case EmitLitHdrVType::NO_IDX_V:
 | 
	
		
			
				|  |  | +      GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX_V();
 | 
	
		
			
				|  |  | +      break;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |    GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED();
 | 
	
		
			
				|  |  | -  uint32_t len_key = static_cast<uint32_t> GRPC_SLICE_LENGTH(GRPC_MDKEY(elem));
 | 
	
		
			
				|  |  | -  wire_value value = get_wire_value<false>(elem, st->use_true_binary_metadata);
 | 
	
		
			
				|  |  | -  uint32_t len_val = static_cast<uint32_t>(wire_value_length(value));
 | 
	
		
			
				|  |  | -  uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1);
 | 
	
		
			
				|  |  | -  uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1);
 | 
	
		
			
				|  |  | -  GPR_ASSERT(len_key <= UINT32_MAX);
 | 
	
		
			
				|  |  | -  GPR_ASSERT(wire_value_length(value) <= UINT32_MAX);
 | 
	
		
			
				|  |  | -  /* Preconditions passed; emit header. */
 | 
	
		
			
				|  |  | -  uint8_t* data = add_tiny_header_data(st, 1 + len_key_len);
 | 
	
		
			
				|  |  | -  data[0] = 0x00;
 | 
	
		
			
				|  |  | -  GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, &data[1], len_key_len);
 | 
	
		
			
				|  |  | +  const uint32_t len_key =
 | 
	
		
			
				|  |  | +      static_cast<uint32_t>(GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)));
 | 
	
		
			
				|  |  | +  const wire_value value =
 | 
	
		
			
				|  |  | +      type == EmitLitHdrVType::INC_IDX_V
 | 
	
		
			
				|  |  | +          ? get_wire_value<true>(elem, st->use_true_binary_metadata)
 | 
	
		
			
				|  |  | +          : get_wire_value<false>(elem, st->use_true_binary_metadata);
 | 
	
		
			
				|  |  | +  const uint32_t len_val = wire_value_length(value);
 | 
	
		
			
				|  |  | +  const uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1);
 | 
	
		
			
				|  |  | +  const uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1);
 | 
	
		
			
				|  |  | +  GPR_DEBUG_ASSERT(len_key <= UINT32_MAX);
 | 
	
		
			
				|  |  | +  GPR_DEBUG_ASSERT(1 + len_key_len < GRPC_SLICE_INLINED_SIZE);
 | 
	
		
			
				|  |  | +  uint8_t* key_buf = add_tiny_header_data(st, 1 + len_key_len);
 | 
	
		
			
				|  |  | +  key_buf[0] = type == EmitLitHdrVType::INC_IDX_V ? 0x40 : 0x00;
 | 
	
		
			
				|  |  | +  GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, &key_buf[1], len_key_len);
 | 
	
		
			
				|  |  |    add_header_data(st, grpc_slice_ref_internal(GRPC_MDKEY(elem)));
 | 
	
		
			
				|  |  | -  GRPC_CHTTP2_WRITE_VARINT(len_val, 1, value.huffman_prefix,
 | 
	
		
			
				|  |  | -                           add_tiny_header_data(st, len_val_len), len_val_len);
 | 
	
		
			
				|  |  | -  add_wire_value(st, value);
 | 
	
		
			
				|  |  | +  uint8_t* value_buf = add_tiny_header_data(
 | 
	
		
			
				|  |  | +      st, len_val_len + (value.insert_null_before_wire_value ? 1 : 0));
 | 
	
		
			
				|  |  | +  GRPC_CHTTP2_WRITE_VARINT(len_val, 1, value.huffman_prefix, value_buf,
 | 
	
		
			
				|  |  | +                           len_val_len);
 | 
	
		
			
				|  |  | +  if (value.insert_null_before_wire_value) {
 | 
	
		
			
				|  |  | +    value_buf[len_val_len] = 0;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  add_header_data(st, value.data);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void emit_advertise_table_size_change(grpc_chttp2_hpack_compressor* c,
 | 
	
	
		
			
				|  | @@ -461,113 +474,142 @@ static void emit_advertise_table_size_change(grpc_chttp2_hpack_compressor* c,
 | 
	
		
			
				|  |  |    c->advertise_table_size_change = 0;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static void GPR_ATTRIBUTE_NOINLINE hpack_enc_log(grpc_mdelem elem) {
 | 
	
		
			
				|  |  | +  char* k = grpc_slice_to_c_string(GRPC_MDKEY(elem));
 | 
	
		
			
				|  |  | +  char* v = nullptr;
 | 
	
		
			
				|  |  | +  if (grpc_is_binary_header_internal(GRPC_MDKEY(elem))) {
 | 
	
		
			
				|  |  | +    v = grpc_dump_slice(GRPC_MDVALUE(elem), GPR_DUMP_HEX);
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    v = grpc_slice_to_c_string(GRPC_MDVALUE(elem));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  gpr_log(
 | 
	
		
			
				|  |  | +      GPR_INFO,
 | 
	
		
			
				|  |  | +      "Encode: '%s: %s', elem_interned=%d [%d], k_interned=%d, v_interned=%d",
 | 
	
		
			
				|  |  | +      k, v, GRPC_MDELEM_IS_INTERNED(elem), GRPC_MDELEM_STORAGE(elem),
 | 
	
		
			
				|  |  | +      grpc_slice_is_interned(GRPC_MDKEY(elem)),
 | 
	
		
			
				|  |  | +      grpc_slice_is_interned(GRPC_MDVALUE(elem)));
 | 
	
		
			
				|  |  | +  gpr_free(k);
 | 
	
		
			
				|  |  | +  gpr_free(v);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  static uint32_t dynidx(grpc_chttp2_hpack_compressor* c, uint32_t elem_index) {
 | 
	
		
			
				|  |  |    return 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY + c->tail_remote_index +
 | 
	
		
			
				|  |  |           c->table_elems - elem_index;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +struct EmitIndexedStatus {
 | 
	
		
			
				|  |  | +  EmitIndexedStatus() = default;
 | 
	
		
			
				|  |  | +  EmitIndexedStatus(uint32_t elem_hash, bool emitted, bool can_add)
 | 
	
		
			
				|  |  | +      : elem_hash(elem_hash), emitted(emitted), can_add(can_add) {}
 | 
	
		
			
				|  |  | +  const uint32_t elem_hash = 0;
 | 
	
		
			
				|  |  | +  const bool emitted = false;
 | 
	
		
			
				|  |  | +  const bool can_add = false;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static EmitIndexedStatus maybe_emit_indexed(grpc_chttp2_hpack_compressor* c,
 | 
	
		
			
				|  |  | +                                            grpc_mdelem elem,
 | 
	
		
			
				|  |  | +                                            framer_state* st) {
 | 
	
		
			
				|  |  | +  const uint32_t elem_hash =
 | 
	
		
			
				|  |  | +      GRPC_MDELEM_STORAGE(elem) == GRPC_MDELEM_STORAGE_INTERNED
 | 
	
		
			
				|  |  | +          ? reinterpret_cast<grpc_core::InternedMetadata*>(
 | 
	
		
			
				|  |  | +                GRPC_MDELEM_DATA(elem))
 | 
	
		
			
				|  |  | +                ->hash()
 | 
	
		
			
				|  |  | +          : reinterpret_cast<grpc_core::StaticMetadata*>(GRPC_MDELEM_DATA(elem))
 | 
	
		
			
				|  |  | +                ->hash();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  inc_filter(HASH_FRAGMENT_1(elem_hash), &c->filter_elems_sum, c->filter_elems);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /* is this elem currently in the decoders table? */
 | 
	
		
			
				|  |  | +  if (grpc_mdelem_both_interned_eq(c->entries_elems[HASH_FRAGMENT_2(elem_hash)],
 | 
	
		
			
				|  |  | +                                   elem) &&
 | 
	
		
			
				|  |  | +      c->indices_elems[HASH_FRAGMENT_2(elem_hash)] > c->tail_remote_index) {
 | 
	
		
			
				|  |  | +    /* HIT: complete element (first cuckoo hash) */
 | 
	
		
			
				|  |  | +    emit_indexed(c, dynidx(c, c->indices_elems[HASH_FRAGMENT_2(elem_hash)]),
 | 
	
		
			
				|  |  | +                 st);
 | 
	
		
			
				|  |  | +    return EmitIndexedStatus(elem_hash, true, false);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (grpc_mdelem_both_interned_eq(c->entries_elems[HASH_FRAGMENT_3(elem_hash)],
 | 
	
		
			
				|  |  | +                                   elem) &&
 | 
	
		
			
				|  |  | +      c->indices_elems[HASH_FRAGMENT_3(elem_hash)] > c->tail_remote_index) {
 | 
	
		
			
				|  |  | +    /* HIT: complete element (second cuckoo hash) */
 | 
	
		
			
				|  |  | +    emit_indexed(c, dynidx(c, c->indices_elems[HASH_FRAGMENT_3(elem_hash)]),
 | 
	
		
			
				|  |  | +                 st);
 | 
	
		
			
				|  |  | +    return EmitIndexedStatus(elem_hash, true, false);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  const bool can_add = c->filter_elems[HASH_FRAGMENT_1(elem_hash)] >=
 | 
	
		
			
				|  |  | +                       c->filter_elems_sum / ONE_ON_ADD_PROBABILITY;
 | 
	
		
			
				|  |  | +  return EmitIndexedStatus(elem_hash, false, can_add);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static void emit_maybe_add(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem,
 | 
	
		
			
				|  |  | +                           framer_state* st, uint32_t indices_key,
 | 
	
		
			
				|  |  | +                           bool should_add_elem, size_t decoder_space_usage,
 | 
	
		
			
				|  |  | +                           uint32_t elem_hash, uint32_t key_hash) {
 | 
	
		
			
				|  |  | +  if (should_add_elem) {
 | 
	
		
			
				|  |  | +    emit_lithdr<EmitLitHdrType::INC_IDX>(c, dynidx(c, indices_key), elem, st);
 | 
	
		
			
				|  |  | +    add_elem(c, elem, decoder_space_usage, elem_hash, key_hash);
 | 
	
		
			
				|  |  | +  } else {
 | 
	
		
			
				|  |  | +    emit_lithdr<EmitLitHdrType::NO_IDX>(c, dynidx(c, indices_key), elem, st);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  /* encode an mdelem */
 | 
	
		
			
				|  |  |  static void hpack_enc(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem,
 | 
	
		
			
				|  |  |                        framer_state* st) {
 | 
	
		
			
				|  |  | -  GPR_ASSERT(GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)) > 0);
 | 
	
		
			
				|  |  | +  /* User-provided key len validated in grpc_validate_header_key_is_legal(). */
 | 
	
		
			
				|  |  | +  GPR_DEBUG_ASSERT(GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)) > 0);
 | 
	
		
			
				|  |  | +  /* Header ordering: all reserved headers (prefixed with ':') must precede
 | 
	
		
			
				|  |  | +   * regular headers. This can be a debug assert, since:
 | 
	
		
			
				|  |  | +   * 1) User cannot give us ':' headers (grpc_validate_header_key_is_legal()).
 | 
	
		
			
				|  |  | +   * 2) grpc filters/core should be checked during debug builds. */
 | 
	
		
			
				|  |  | +#ifndef NDEBUG
 | 
	
		
			
				|  |  |    if (GRPC_SLICE_START_PTR(GRPC_MDKEY(elem))[0] != ':') { /* regular header */
 | 
	
		
			
				|  |  |      st->seen_regular_header = 1;
 | 
	
		
			
				|  |  |    } else {
 | 
	
		
			
				|  |  | -    GPR_ASSERT(
 | 
	
		
			
				|  |  | +    GPR_DEBUG_ASSERT(
 | 
	
		
			
				|  |  |          st->seen_regular_header == 0 &&
 | 
	
		
			
				|  |  |          "Reserved header (colon-prefixed) happening after regular ones.");
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |    if (GRPC_TRACE_FLAG_ENABLED(grpc_http_trace)) {
 | 
	
		
			
				|  |  | -    char* k = grpc_slice_to_c_string(GRPC_MDKEY(elem));
 | 
	
		
			
				|  |  | -    char* v = nullptr;
 | 
	
		
			
				|  |  | -    if (grpc_is_binary_header_internal(GRPC_MDKEY(elem))) {
 | 
	
		
			
				|  |  | -      v = grpc_dump_slice(GRPC_MDVALUE(elem), GPR_DUMP_HEX);
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      v = grpc_slice_to_c_string(GRPC_MDVALUE(elem));
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    gpr_log(
 | 
	
		
			
				|  |  | -        GPR_INFO,
 | 
	
		
			
				|  |  | -        "Encode: '%s: %s', elem_interned=%d [%d], k_interned=%d, v_interned=%d",
 | 
	
		
			
				|  |  | -        k, v, GRPC_MDELEM_IS_INTERNED(elem), GRPC_MDELEM_STORAGE(elem),
 | 
	
		
			
				|  |  | -        grpc_slice_is_interned(GRPC_MDKEY(elem)),
 | 
	
		
			
				|  |  | -        grpc_slice_is_interned(GRPC_MDVALUE(elem)));
 | 
	
		
			
				|  |  | -    gpr_free(k);
 | 
	
		
			
				|  |  | -    gpr_free(v);
 | 
	
		
			
				|  |  | +    hpack_enc_log(elem);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  bool elem_interned = GRPC_MDELEM_IS_INTERNED(elem);
 | 
	
		
			
				|  |  | -  bool key_interned = elem_interned || grpc_slice_is_interned(GRPC_MDKEY(elem));
 | 
	
		
			
				|  |  | +  const bool elem_interned = GRPC_MDELEM_IS_INTERNED(elem);
 | 
	
		
			
				|  |  | +  const bool key_interned =
 | 
	
		
			
				|  |  | +      elem_interned || grpc_slice_is_interned(GRPC_MDKEY(elem));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  // Key is not interned, emit literals.
 | 
	
		
			
				|  |  | +  /* Key is not interned, emit literals. */
 | 
	
		
			
				|  |  |    if (!key_interned) {
 | 
	
		
			
				|  |  | -    emit_lithdr_noidx_v(c, 0, elem, st);
 | 
	
		
			
				|  |  | +    emit_lithdr_v<EmitLitHdrVType::NO_IDX_V>(c, elem, st);
 | 
	
		
			
				|  |  |      return;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  uint32_t elem_hash = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (elem_interned) {
 | 
	
		
			
				|  |  | -    if (GRPC_MDELEM_STORAGE(elem) == GRPC_MDELEM_STORAGE_INTERNED) {
 | 
	
		
			
				|  |  | -      elem_hash =
 | 
	
		
			
				|  |  | -          reinterpret_cast<grpc_core::InternedMetadata*>(GRPC_MDELEM_DATA(elem))
 | 
	
		
			
				|  |  | -              ->hash();
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      elem_hash =
 | 
	
		
			
				|  |  | -          reinterpret_cast<grpc_core::StaticMetadata*>(GRPC_MDELEM_DATA(elem))
 | 
	
		
			
				|  |  | -              ->hash();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    inc_filter(HASH_FRAGMENT_1(elem_hash), &c->filter_elems_sum,
 | 
	
		
			
				|  |  | -               c->filter_elems);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /* is this elem currently in the decoders table? */
 | 
	
		
			
				|  |  | -    if (grpc_mdelem_both_interned_eq(
 | 
	
		
			
				|  |  | -            c->entries_elems[HASH_FRAGMENT_2(elem_hash)], elem) &&
 | 
	
		
			
				|  |  | -        c->indices_elems[HASH_FRAGMENT_2(elem_hash)] > c->tail_remote_index) {
 | 
	
		
			
				|  |  | -      /* HIT: complete element (first cuckoo hash) */
 | 
	
		
			
				|  |  | -      emit_indexed(c, dynidx(c, c->indices_elems[HASH_FRAGMENT_2(elem_hash)]),
 | 
	
		
			
				|  |  | -                   st);
 | 
	
		
			
				|  |  | -      return;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    if (grpc_mdelem_both_interned_eq(
 | 
	
		
			
				|  |  | -            c->entries_elems[HASH_FRAGMENT_3(elem_hash)], elem) &&
 | 
	
		
			
				|  |  | -        c->indices_elems[HASH_FRAGMENT_3(elem_hash)] > c->tail_remote_index) {
 | 
	
		
			
				|  |  | -      /* HIT: complete element (second cuckoo hash) */
 | 
	
		
			
				|  |  | -      emit_indexed(c, dynidx(c, c->indices_elems[HASH_FRAGMENT_3(elem_hash)]),
 | 
	
		
			
				|  |  | -                   st);
 | 
	
		
			
				|  |  | -      return;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +  /* Interned metadata => maybe already indexed. */
 | 
	
		
			
				|  |  | +  const EmitIndexedStatus ret =
 | 
	
		
			
				|  |  | +      elem_interned ? maybe_emit_indexed(c, elem, st) : EmitIndexedStatus();
 | 
	
		
			
				|  |  | +  if (ret.emitted) {
 | 
	
		
			
				|  |  | +    return;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  uint32_t indices_key;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |    /* should this elem be in the table? */
 | 
	
		
			
				|  |  |    const size_t decoder_space_usage =
 | 
	
		
			
				|  |  |        grpc_chttp2_get_size_in_hpack_table(elem, st->use_true_binary_metadata);
 | 
	
		
			
				|  |  | -  const bool should_add_elem = elem_interned &&
 | 
	
		
			
				|  |  | -                               decoder_space_usage < MAX_DECODER_SPACE_USAGE &&
 | 
	
		
			
				|  |  | -                               c->filter_elems[HASH_FRAGMENT_1(elem_hash)] >=
 | 
	
		
			
				|  |  | -                                   c->filter_elems_sum / ONE_ON_ADD_PROBABILITY;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  uint32_t key_hash = GRPC_MDKEY(elem).refcount->Hash(GRPC_MDKEY(elem));
 | 
	
		
			
				|  |  | -  auto emit_maybe_add = [&should_add_elem, &elem, &st, &c, &indices_key,
 | 
	
		
			
				|  |  | -                         &decoder_space_usage, &elem_hash, &key_hash] {
 | 
	
		
			
				|  |  | -    if (should_add_elem) {
 | 
	
		
			
				|  |  | -      emit_lithdr_incidx(c, dynidx(c, indices_key), elem, st);
 | 
	
		
			
				|  |  | -      add_elem(c, elem, decoder_space_usage, elem_hash, key_hash);
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      emit_lithdr_noidx(c, dynidx(c, indices_key), elem, st);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  };
 | 
	
		
			
				|  |  | +  const bool decoder_space_available =
 | 
	
		
			
				|  |  | +      decoder_space_usage < MAX_DECODER_SPACE_USAGE;
 | 
	
		
			
				|  |  | +  const bool should_add_elem =
 | 
	
		
			
				|  |  | +      elem_interned && decoder_space_available && ret.can_add;
 | 
	
		
			
				|  |  | +  const uint32_t elem_hash = ret.elem_hash;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* no hits for the elem... maybe there's a key? */
 | 
	
		
			
				|  |  | -  indices_key = c->indices_keys[HASH_FRAGMENT_2(key_hash)];
 | 
	
		
			
				|  |  | +  const uint32_t key_hash = GRPC_MDKEY(elem).refcount->Hash(GRPC_MDKEY(elem));
 | 
	
		
			
				|  |  | +  uint32_t indices_key = c->indices_keys[HASH_FRAGMENT_2(key_hash)];
 | 
	
		
			
				|  |  |    if (grpc_slice_static_interned_equal(
 | 
	
		
			
				|  |  |            c->entries_keys[HASH_FRAGMENT_2(key_hash)], GRPC_MDKEY(elem)) &&
 | 
	
		
			
				|  |  |        indices_key > c->tail_remote_index) {
 | 
	
		
			
				|  |  |      /* HIT: key (first cuckoo hash) */
 | 
	
		
			
				|  |  | -    emit_maybe_add();
 | 
	
		
			
				|  |  | +    emit_maybe_add(c, elem, st, indices_key, should_add_elem,
 | 
	
		
			
				|  |  | +                   decoder_space_usage, elem_hash, key_hash);
 | 
	
		
			
				|  |  |      return;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -575,18 +617,18 @@ static void hpack_enc(grpc_chttp2_hpack_compressor* c, grpc_mdelem elem,
 | 
	
		
			
				|  |  |    if (grpc_slice_static_interned_equal(
 | 
	
		
			
				|  |  |            c->entries_keys[HASH_FRAGMENT_3(key_hash)], GRPC_MDKEY(elem)) &&
 | 
	
		
			
				|  |  |        indices_key > c->tail_remote_index) {
 | 
	
		
			
				|  |  | -    /* HIT: key (first cuckoo hash) */
 | 
	
		
			
				|  |  | -    emit_maybe_add();
 | 
	
		
			
				|  |  | +    /* HIT: key (second cuckoo hash) */
 | 
	
		
			
				|  |  | +    emit_maybe_add(c, elem, st, indices_key, should_add_elem,
 | 
	
		
			
				|  |  | +                   decoder_space_usage, elem_hash, key_hash);
 | 
	
		
			
				|  |  |      return;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    /* no elem, key in the table... fall back to literal emission */
 | 
	
		
			
				|  |  | -  const bool should_add_key =
 | 
	
		
			
				|  |  | -      !elem_interned && decoder_space_usage < MAX_DECODER_SPACE_USAGE;
 | 
	
		
			
				|  |  | +  const bool should_add_key = !elem_interned && decoder_space_available;
 | 
	
		
			
				|  |  |    if (should_add_elem || should_add_key) {
 | 
	
		
			
				|  |  | -    emit_lithdr_incidx_v(c, 0, elem, st);
 | 
	
		
			
				|  |  | +    emit_lithdr_v<EmitLitHdrVType::INC_IDX_V>(c, elem, st);
 | 
	
		
			
				|  |  |    } else {
 | 
	
		
			
				|  |  | -    emit_lithdr_noidx_v(c, 0, elem, st);
 | 
	
		
			
				|  |  | +    emit_lithdr_v<EmitLitHdrVType::NO_IDX_V>(c, elem, st);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    if (should_add_elem) {
 | 
	
		
			
				|  |  |      add_elem(c, elem, decoder_space_usage, elem_hash, key_hash);
 | 
	
	
		
			
				|  | @@ -695,8 +737,12 @@ void grpc_chttp2_encode_header(grpc_chttp2_hpack_compressor* c,
 | 
	
		
			
				|  |  |                                 grpc_metadata_batch* metadata,
 | 
	
		
			
				|  |  |                                 const grpc_encode_header_options* options,
 | 
	
		
			
				|  |  |                                 grpc_slice_buffer* outbuf) {
 | 
	
		
			
				|  |  | -  GPR_ASSERT(options->stream_id != 0);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +  /* grpc_chttp2_encode_header is called by FlushInitial/TrailingMetadata in
 | 
	
		
			
				|  |  | +     writing.cc. Specifically, on streams returned by NextStream(), which
 | 
	
		
			
				|  |  | +     returns streams from the list GRPC_CHTTP2_LIST_WRITABLE. The only way to be
 | 
	
		
			
				|  |  | +     added to the list is via  grpc_chttp2_list_add_writable_stream(), which
 | 
	
		
			
				|  |  | +     validates that stream_id is not 0. So, this can be a debug assert. */
 | 
	
		
			
				|  |  | +  GPR_DEBUG_ASSERT(options->stream_id != 0);
 | 
	
		
			
				|  |  |    framer_state st;
 | 
	
		
			
				|  |  |    st.seen_regular_header = 0;
 | 
	
		
			
				|  |  |    st.stream_id = options->stream_id;
 |