• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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