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()18 QpackInstructionEncoder::QpackInstructionEncoder()
19 : use_huffman_(false),
20 string_length_(0),
21 byte_(0),
22 state_(State::kOpcode),
23 instruction_(nullptr) {}
24
Encode(const QpackInstructionWithValues & instruction_with_values,std::string * output)25 void QpackInstructionEncoder::Encode(
26 const QpackInstructionWithValues& instruction_with_values,
27 std::string* output) {
28 QUICHE_DCHECK(instruction_with_values.instruction());
29
30 state_ = State::kOpcode;
31 instruction_ = instruction_with_values.instruction();
32 field_ = instruction_->fields.begin();
33
34 // Field list must not be empty.
35 QUICHE_DCHECK(field_ != instruction_->fields.end());
36
37 do {
38 switch (state_) {
39 case State::kOpcode:
40 DoOpcode();
41 break;
42 case State::kStartField:
43 DoStartField();
44 break;
45 case State::kSbit:
46 DoSBit(instruction_with_values.s_bit());
47 break;
48 case State::kVarintEncode:
49 DoVarintEncode(instruction_with_values.varint(),
50 instruction_with_values.varint2(), output);
51 break;
52 case State::kStartString:
53 DoStartString(instruction_with_values.name(),
54 instruction_with_values.value());
55 break;
56 case State::kWriteString:
57 DoWriteString(instruction_with_values.name(),
58 instruction_with_values.value(), output);
59 break;
60 }
61 } while (field_ != instruction_->fields.end());
62
63 QUICHE_DCHECK(state_ == State::kStartField);
64 }
65
DoOpcode()66 void QpackInstructionEncoder::DoOpcode() {
67 QUICHE_DCHECK_EQ(0u, byte_);
68
69 byte_ = instruction_->opcode.value;
70
71 state_ = State::kStartField;
72 }
73
DoStartField()74 void QpackInstructionEncoder::DoStartField() {
75 switch (field_->type) {
76 case QpackInstructionFieldType::kSbit:
77 state_ = State::kSbit;
78 return;
79 case QpackInstructionFieldType::kVarint:
80 case QpackInstructionFieldType::kVarint2:
81 state_ = State::kVarintEncode;
82 return;
83 case QpackInstructionFieldType::kName:
84 case QpackInstructionFieldType::kValue:
85 state_ = State::kStartString;
86 return;
87 }
88 }
89
DoSBit(bool s_bit)90 void QpackInstructionEncoder::DoSBit(bool s_bit) {
91 QUICHE_DCHECK(field_->type == QpackInstructionFieldType::kSbit);
92
93 if (s_bit) {
94 QUICHE_DCHECK_EQ(0, byte_ & field_->param);
95
96 byte_ |= field_->param;
97 }
98
99 ++field_;
100 state_ = State::kStartField;
101 }
102
DoVarintEncode(uint64_t varint,uint64_t varint2,std::string * output)103 void QpackInstructionEncoder::DoVarintEncode(uint64_t varint, uint64_t varint2,
104 std::string* output) {
105 QUICHE_DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
106 field_->type == QpackInstructionFieldType::kVarint2 ||
107 field_->type == QpackInstructionFieldType::kName ||
108 field_->type == QpackInstructionFieldType::kValue);
109 uint64_t integer_to_encode;
110 switch (field_->type) {
111 case QpackInstructionFieldType::kVarint:
112 integer_to_encode = varint;
113 break;
114 case QpackInstructionFieldType::kVarint2:
115 integer_to_encode = varint2;
116 break;
117 default:
118 integer_to_encode = string_length_;
119 break;
120 }
121
122 http2::HpackVarintEncoder::Encode(byte_, field_->param, integer_to_encode,
123 output);
124 byte_ = 0;
125
126 if (field_->type == QpackInstructionFieldType::kVarint ||
127 field_->type == QpackInstructionFieldType::kVarint2) {
128 ++field_;
129 state_ = State::kStartField;
130 return;
131 }
132
133 state_ = State::kWriteString;
134 }
135
DoStartString(absl::string_view name,absl::string_view value)136 void QpackInstructionEncoder::DoStartString(absl::string_view name,
137 absl::string_view value) {
138 QUICHE_DCHECK(field_->type == QpackInstructionFieldType::kName ||
139 field_->type == QpackInstructionFieldType::kValue);
140
141 absl::string_view string_to_write =
142 (field_->type == QpackInstructionFieldType::kName) ? name : value;
143 string_length_ = string_to_write.size();
144
145 size_t encoded_size = http2::HuffmanSize(string_to_write);
146 use_huffman_ = encoded_size < string_length_;
147
148 if (use_huffman_) {
149 QUICHE_DCHECK_EQ(0, byte_ & (1 << field_->param));
150 byte_ |= (1 << field_->param);
151
152 string_length_ = encoded_size;
153 }
154
155 state_ = State::kVarintEncode;
156 }
157
DoWriteString(absl::string_view name,absl::string_view value,std::string * output)158 void QpackInstructionEncoder::DoWriteString(absl::string_view name,
159 absl::string_view value,
160 std::string* output) {
161 QUICHE_DCHECK(field_->type == QpackInstructionFieldType::kName ||
162 field_->type == QpackInstructionFieldType::kValue);
163
164 absl::string_view string_to_write =
165 (field_->type == QpackInstructionFieldType::kName) ? name : value;
166 if (use_huffman_) {
167 http2::HuffmanEncodeFast(string_to_write, string_length_, output);
168 } else {
169 absl::StrAppend(output, string_to_write);
170 }
171
172 ++field_;
173 state_ = State::kStartField;
174 }
175
176 } // namespace quic
177