xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/qpack/qpack_instruction_encoder.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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