1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/core/qpack/qpack_instruction_encoder.h"
6
7 #include <limits>
8
9 #include "absl/strings/str_cat.h"
10 #include "absl/strings/string_view.h"
11 #include "quiche/http2/hpack/huffman/hpack_huffman_encoder.h"
12 #include "quiche/http2/hpack/varint/hpack_varint_encoder.h"
13 #include "quiche/quic/platform/api/quic_flags.h"
14 #include "quiche/quic/platform/api/quic_logging.h"
15
16 namespace quic {
17
QpackInstructionEncoder(HuffmanEncoding huffman_encoding)18 QpackInstructionEncoder::QpackInstructionEncoder(
19 HuffmanEncoding huffman_encoding)
20 : huffman_encoding_(huffman_encoding),
21 use_huffman_(false),
22 string_length_(0),
23 byte_(0),
24 state_(State::kOpcode),
25 instruction_(nullptr) {}
26
Encode(const QpackInstructionWithValues & instruction_with_values,std::string * output)27 void QpackInstructionEncoder::Encode(
28 const QpackInstructionWithValues& instruction_with_values,
29 std::string* output) {
30 QUICHE_DCHECK(instruction_with_values.instruction());
31
32 state_ = State::kOpcode;
33 instruction_ = instruction_with_values.instruction();
34 field_ = instruction_->fields.begin();
35
36 // Field list must not be empty.
37 QUICHE_DCHECK(field_ != instruction_->fields.end());
38
39 do {
40 switch (state_) {
41 case State::kOpcode:
42 DoOpcode();
43 break;
44 case State::kStartField:
45 DoStartField();
46 break;
47 case State::kSbit:
48 DoSBit(instruction_with_values.s_bit());
49 break;
50 case State::kVarintEncode:
51 DoVarintEncode(instruction_with_values.varint(),
52 instruction_with_values.varint2(), output);
53 break;
54 case State::kStartString:
55 DoStartString(instruction_with_values.name(),
56 instruction_with_values.value());
57 break;
58 case State::kWriteString:
59 DoWriteString(instruction_with_values.name(),
60 instruction_with_values.value(), output);
61 break;
62 }
63 } while (field_ != instruction_->fields.end());
64
65 QUICHE_DCHECK(state_ == State::kStartField);
66 }
67
DoOpcode()68 void QpackInstructionEncoder::DoOpcode() {
69 QUICHE_DCHECK_EQ(0u, byte_);
70
71 byte_ = instruction_->opcode.value;
72
73 state_ = State::kStartField;
74 }
75
DoStartField()76 void QpackInstructionEncoder::DoStartField() {
77 switch (field_->type) {
78 case QpackInstructionFieldType::kSbit:
79 state_ = State::kSbit;
80 return;
81 case QpackInstructionFieldType::kVarint:
82 case QpackInstructionFieldType::kVarint2:
83 state_ = State::kVarintEncode;
84 return;
85 case QpackInstructionFieldType::kName:
86 case QpackInstructionFieldType::kValue:
87 state_ = State::kStartString;
88 return;
89 }
90 }
91
DoSBit(bool s_bit)92 void QpackInstructionEncoder::DoSBit(bool s_bit) {
93 QUICHE_DCHECK(field_->type == QpackInstructionFieldType::kSbit);
94
95 if (s_bit) {
96 QUICHE_DCHECK_EQ(0, byte_ & field_->param);
97
98 byte_ |= field_->param;
99 }
100
101 ++field_;
102 state_ = State::kStartField;
103 }
104
DoVarintEncode(uint64_t varint,uint64_t varint2,std::string * output)105 void QpackInstructionEncoder::DoVarintEncode(uint64_t varint, uint64_t varint2,
106 std::string* output) {
107 QUICHE_DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
108 field_->type == QpackInstructionFieldType::kVarint2 ||
109 field_->type == QpackInstructionFieldType::kName ||
110 field_->type == QpackInstructionFieldType::kValue);
111 uint64_t integer_to_encode;
112 switch (field_->type) {
113 case QpackInstructionFieldType::kVarint:
114 integer_to_encode = varint;
115 break;
116 case QpackInstructionFieldType::kVarint2:
117 integer_to_encode = varint2;
118 break;
119 default:
120 integer_to_encode = string_length_;
121 break;
122 }
123
124 http2::HpackVarintEncoder::Encode(byte_, field_->param, integer_to_encode,
125 output);
126 byte_ = 0;
127
128 if (field_->type == QpackInstructionFieldType::kVarint ||
129 field_->type == QpackInstructionFieldType::kVarint2) {
130 ++field_;
131 state_ = State::kStartField;
132 return;
133 }
134
135 state_ = State::kWriteString;
136 }
137
DoStartString(absl::string_view name,absl::string_view value)138 void QpackInstructionEncoder::DoStartString(absl::string_view name,
139 absl::string_view value) {
140 QUICHE_DCHECK(field_->type == QpackInstructionFieldType::kName ||
141 field_->type == QpackInstructionFieldType::kValue);
142
143 absl::string_view string_to_write =
144 (field_->type == QpackInstructionFieldType::kName) ? name : value;
145 string_length_ = string_to_write.size();
146
147 if (huffman_encoding_ == HuffmanEncoding::kEnabled) {
148 size_t encoded_size = http2::HuffmanSize(string_to_write);
149 use_huffman_ = encoded_size < string_length_;
150
151 if (use_huffman_) {
152 QUICHE_DCHECK_EQ(0, byte_ & (1 << field_->param));
153 byte_ |= (1 << field_->param);
154
155 string_length_ = encoded_size;
156 }
157 }
158 state_ = State::kVarintEncode;
159 }
160
DoWriteString(absl::string_view name,absl::string_view value,std::string * output)161 void QpackInstructionEncoder::DoWriteString(absl::string_view name,
162 absl::string_view value,
163 std::string* output) {
164 QUICHE_DCHECK(field_->type == QpackInstructionFieldType::kName ||
165 field_->type == QpackInstructionFieldType::kValue);
166
167 absl::string_view string_to_write =
168 (field_->type == QpackInstructionFieldType::kName) ? name : value;
169 if (use_huffman_) {
170 http2::HuffmanEncodeFast(string_to_write, string_length_, output);
171 } else {
172 absl::StrAppend(output, string_to_write);
173 }
174
175 ++field_;
176 state_ = State::kStartField;
177 }
178
179 } // namespace quic
180