|  | // Copyright 2018 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h" | 
|  |  | 
|  | #include <limits> | 
|  |  | 
|  | #include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.h" | 
|  | #include "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.h" | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_string_utils.h" | 
|  | #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" | 
|  |  | 
|  | namespace quic { | 
|  |  | 
|  | QpackInstructionEncoder::QpackInstructionEncoder() | 
|  | : byte_(0), state_(State::kOpcode), instruction_(nullptr) {} | 
|  |  | 
|  | void QpackInstructionEncoder::Encode( | 
|  | const QpackInstructionWithValues& instruction_with_values, | 
|  | std::string* output) { | 
|  | DCHECK(instruction_with_values.instruction()); | 
|  |  | 
|  | state_ = State::kOpcode; | 
|  | instruction_ = instruction_with_values.instruction(); | 
|  | field_ = instruction_->fields.begin(); | 
|  |  | 
|  | // Field list must not be empty. | 
|  | DCHECK(field_ != instruction_->fields.end()); | 
|  |  | 
|  | do { | 
|  | switch (state_) { | 
|  | case State::kOpcode: | 
|  | DoOpcode(); | 
|  | break; | 
|  | case State::kStartField: | 
|  | DoStartField(); | 
|  | break; | 
|  | case State::kSbit: | 
|  | DoSBit(instruction_with_values.s_bit()); | 
|  | break; | 
|  | case State::kVarintEncode: | 
|  | DoVarintEncode(instruction_with_values.varint(), | 
|  | instruction_with_values.varint2(), output); | 
|  | break; | 
|  | case State::kStartString: | 
|  | DoStartString(instruction_with_values.name(), | 
|  | instruction_with_values.value()); | 
|  | break; | 
|  | case State::kWriteString: | 
|  | DoWriteString(output); | 
|  | break; | 
|  | } | 
|  | } while (field_ != instruction_->fields.end()); | 
|  |  | 
|  | DCHECK(state_ == State::kStartField); | 
|  | } | 
|  |  | 
|  | void QpackInstructionEncoder::DoOpcode() { | 
|  | DCHECK_EQ(0u, byte_); | 
|  |  | 
|  | byte_ = instruction_->opcode.value; | 
|  |  | 
|  | state_ = State::kStartField; | 
|  | } | 
|  |  | 
|  | void QpackInstructionEncoder::DoStartField() { | 
|  | switch (field_->type) { | 
|  | case QpackInstructionFieldType::kSbit: | 
|  | state_ = State::kSbit; | 
|  | return; | 
|  | case QpackInstructionFieldType::kVarint: | 
|  | case QpackInstructionFieldType::kVarint2: | 
|  | state_ = State::kVarintEncode; | 
|  | return; | 
|  | case QpackInstructionFieldType::kName: | 
|  | case QpackInstructionFieldType::kValue: | 
|  | state_ = State::kStartString; | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | void QpackInstructionEncoder::DoSBit(bool s_bit) { | 
|  | DCHECK(field_->type == QpackInstructionFieldType::kSbit); | 
|  |  | 
|  | if (s_bit) { | 
|  | DCHECK_EQ(0, byte_ & field_->param); | 
|  |  | 
|  | byte_ |= field_->param; | 
|  | } | 
|  |  | 
|  | ++field_; | 
|  | state_ = State::kStartField; | 
|  | } | 
|  |  | 
|  | void QpackInstructionEncoder::DoVarintEncode(uint64_t varint, | 
|  | uint64_t varint2, | 
|  | std::string* output) { | 
|  | DCHECK(field_->type == QpackInstructionFieldType::kVarint || | 
|  | field_->type == QpackInstructionFieldType::kVarint2 || | 
|  | field_->type == QpackInstructionFieldType::kName || | 
|  | field_->type == QpackInstructionFieldType::kValue); | 
|  | uint64_t integer_to_encode; | 
|  | switch (field_->type) { | 
|  | case QpackInstructionFieldType::kVarint: | 
|  | integer_to_encode = varint; | 
|  | break; | 
|  | case QpackInstructionFieldType::kVarint2: | 
|  | integer_to_encode = varint2; | 
|  | break; | 
|  | default: | 
|  | integer_to_encode = string_to_write_.size(); | 
|  | break; | 
|  | } | 
|  |  | 
|  | http2::HpackVarintEncoder::Encode(byte_, field_->param, integer_to_encode, | 
|  | output); | 
|  | byte_ = 0; | 
|  |  | 
|  | if (field_->type == QpackInstructionFieldType::kVarint || | 
|  | field_->type == QpackInstructionFieldType::kVarint2) { | 
|  | ++field_; | 
|  | state_ = State::kStartField; | 
|  | return; | 
|  | } | 
|  |  | 
|  | state_ = State::kWriteString; | 
|  | } | 
|  |  | 
|  | void QpackInstructionEncoder::DoStartString(quiche::QuicheStringPiece name, | 
|  | quiche::QuicheStringPiece value) { | 
|  | DCHECK(field_->type == QpackInstructionFieldType::kName || | 
|  | field_->type == QpackInstructionFieldType::kValue); | 
|  |  | 
|  | string_to_write_ = | 
|  | (field_->type == QpackInstructionFieldType::kName) ? name : value; | 
|  | http2::HuffmanEncode(string_to_write_, &huffman_encoded_string_); | 
|  |  | 
|  | if (huffman_encoded_string_.size() < string_to_write_.size()) { | 
|  | DCHECK_EQ(0, byte_ & (1 << field_->param)); | 
|  |  | 
|  | byte_ |= (1 << field_->param); | 
|  | string_to_write_ = huffman_encoded_string_; | 
|  | } | 
|  |  | 
|  | state_ = State::kVarintEncode; | 
|  | } | 
|  |  | 
|  | void QpackInstructionEncoder::DoWriteString(std::string* output) { | 
|  | DCHECK(field_->type == QpackInstructionFieldType::kName || | 
|  | field_->type == QpackInstructionFieldType::kValue); | 
|  |  | 
|  | QuicStrAppend(output, string_to_write_); | 
|  |  | 
|  | ++field_; | 
|  | state_ = State::kStartField; | 
|  | } | 
|  |  | 
|  | }  // namespace quic |