|  | // 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_instructions.h" | 
|  |  | 
|  | #include <limits> | 
|  |  | 
|  | #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" | 
|  | #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" | 
|  |  | 
|  | namespace quic { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Validate that | 
|  | //  * in each instruction, the bits of |value| that are zero in |mask| are zero; | 
|  | //  * every byte matches exactly one opcode. | 
|  | void ValidateLangague(const QpackLanguage* language) { | 
|  | #ifndef NDEBUG | 
|  | for (const auto* instruction : *language) { | 
|  | DCHECK_EQ(0, instruction->opcode.value & ~instruction->opcode.mask); | 
|  | } | 
|  |  | 
|  | for (uint8_t byte = 0; byte < std::numeric_limits<uint8_t>::max(); ++byte) { | 
|  | size_t match_count = 0; | 
|  | for (const auto* instruction : *language) { | 
|  | if ((byte & instruction->opcode.mask) == instruction->opcode.value) { | 
|  | ++match_count; | 
|  | } | 
|  | } | 
|  | DCHECK_EQ(1u, match_count) << static_cast<int>(byte); | 
|  | } | 
|  | #else | 
|  | (void)language; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | bool operator==(const QpackInstructionOpcode& a, | 
|  | const QpackInstructionOpcode& b) { | 
|  | return std::tie(a.value, a.mask) == std::tie(b.value, b.mask); | 
|  | } | 
|  |  | 
|  | const QpackInstruction* InsertWithNameReferenceInstruction() { | 
|  | static const QpackInstructionOpcode* const opcode = | 
|  | new QpackInstructionOpcode{0b10000000, 0b10000000}; | 
|  | static const QpackInstruction* const instruction = | 
|  | new QpackInstruction{*opcode, | 
|  | {{QpackInstructionFieldType::kSbit, 0b01000000}, | 
|  | {QpackInstructionFieldType::kVarint, 6}, | 
|  | {QpackInstructionFieldType::kValue, 7}}}; | 
|  | return instruction; | 
|  | } | 
|  |  | 
|  | const QpackInstruction* InsertWithoutNameReferenceInstruction() { | 
|  | static const QpackInstructionOpcode* const opcode = | 
|  | new QpackInstructionOpcode{0b01000000, 0b11000000}; | 
|  | static const QpackInstruction* const instruction = | 
|  | new QpackInstruction{*opcode, | 
|  | {{QpackInstructionFieldType::kName, 5}, | 
|  | {QpackInstructionFieldType::kValue, 7}}}; | 
|  | return instruction; | 
|  | } | 
|  |  | 
|  | const QpackInstruction* DuplicateInstruction() { | 
|  | static const QpackInstructionOpcode* const opcode = | 
|  | new QpackInstructionOpcode{0b00000000, 0b11100000}; | 
|  | static const QpackInstruction* const instruction = | 
|  | new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 5}}}; | 
|  | return instruction; | 
|  | } | 
|  |  | 
|  | const QpackInstruction* SetDynamicTableCapacityInstruction() { | 
|  | static const QpackInstructionOpcode* const opcode = | 
|  | new QpackInstructionOpcode{0b00100000, 0b11100000}; | 
|  | static const QpackInstruction* const instruction = | 
|  | new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 5}}}; | 
|  | return instruction; | 
|  | } | 
|  |  | 
|  | const QpackLanguage* QpackEncoderStreamLanguage() { | 
|  | static const QpackLanguage* const language = new QpackLanguage{ | 
|  | InsertWithNameReferenceInstruction(), | 
|  | InsertWithoutNameReferenceInstruction(), DuplicateInstruction(), | 
|  | SetDynamicTableCapacityInstruction()}; | 
|  | ValidateLangague(language); | 
|  | return language; | 
|  | } | 
|  |  | 
|  | const QpackInstruction* InsertCountIncrementInstruction() { | 
|  | static const QpackInstructionOpcode* const opcode = | 
|  | new QpackInstructionOpcode{0b00000000, 0b11000000}; | 
|  | static const QpackInstruction* const instruction = | 
|  | new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 6}}}; | 
|  | return instruction; | 
|  | } | 
|  |  | 
|  | const QpackInstruction* HeaderAcknowledgementInstruction() { | 
|  | static const QpackInstructionOpcode* const opcode = | 
|  | new QpackInstructionOpcode{0b10000000, 0b10000000}; | 
|  | static const QpackInstruction* const instruction = | 
|  | new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 7}}}; | 
|  | return instruction; | 
|  | } | 
|  |  | 
|  | const QpackInstruction* StreamCancellationInstruction() { | 
|  | static const QpackInstructionOpcode* const opcode = | 
|  | new QpackInstructionOpcode{0b01000000, 0b11000000}; | 
|  | static const QpackInstruction* const instruction = | 
|  | new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 6}}}; | 
|  | return instruction; | 
|  | } | 
|  |  | 
|  | const QpackLanguage* QpackDecoderStreamLanguage() { | 
|  | static const QpackLanguage* const language = new QpackLanguage{ | 
|  | InsertCountIncrementInstruction(), HeaderAcknowledgementInstruction(), | 
|  | StreamCancellationInstruction()}; | 
|  | ValidateLangague(language); | 
|  | return language; | 
|  | } | 
|  |  | 
|  | const QpackInstruction* QpackPrefixInstruction() { | 
|  | // This opcode matches every input. | 
|  | static const QpackInstructionOpcode* const opcode = | 
|  | new QpackInstructionOpcode{0b00000000, 0b00000000}; | 
|  | static const QpackInstruction* const instruction = | 
|  | new QpackInstruction{*opcode, | 
|  | {{QpackInstructionFieldType::kVarint, 8}, | 
|  | {QpackInstructionFieldType::kSbit, 0b10000000}, | 
|  | {QpackInstructionFieldType::kVarint2, 7}}}; | 
|  | return instruction; | 
|  | } | 
|  |  | 
|  | const QpackLanguage* QpackPrefixLanguage() { | 
|  | static const QpackLanguage* const language = | 
|  | new QpackLanguage{QpackPrefixInstruction()}; | 
|  | ValidateLangague(language); | 
|  | return language; | 
|  | } | 
|  |  | 
|  | const QpackInstruction* QpackIndexedHeaderFieldInstruction() { | 
|  | static const QpackInstructionOpcode* const opcode = | 
|  | new QpackInstructionOpcode{0b10000000, 0b10000000}; | 
|  | static const QpackInstruction* const instruction = | 
|  | new QpackInstruction{*opcode, | 
|  | {{QpackInstructionFieldType::kSbit, 0b01000000}, | 
|  | {QpackInstructionFieldType::kVarint, 6}}}; | 
|  | return instruction; | 
|  | } | 
|  |  | 
|  | const QpackInstruction* QpackIndexedHeaderFieldPostBaseInstruction() { | 
|  | static const QpackInstructionOpcode* const opcode = | 
|  | new QpackInstructionOpcode{0b00010000, 0b11110000}; | 
|  | static const QpackInstruction* const instruction = | 
|  | new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 4}}}; | 
|  | return instruction; | 
|  | } | 
|  |  | 
|  | const QpackInstruction* QpackLiteralHeaderFieldNameReferenceInstruction() { | 
|  | static const QpackInstructionOpcode* const opcode = | 
|  | new QpackInstructionOpcode{0b01000000, 0b11000000}; | 
|  | static const QpackInstruction* const instruction = | 
|  | new QpackInstruction{*opcode, | 
|  | {{QpackInstructionFieldType::kSbit, 0b00010000}, | 
|  | {QpackInstructionFieldType::kVarint, 4}, | 
|  | {QpackInstructionFieldType::kValue, 7}}}; | 
|  | return instruction; | 
|  | } | 
|  |  | 
|  | const QpackInstruction* QpackLiteralHeaderFieldPostBaseInstruction() { | 
|  | static const QpackInstructionOpcode* const opcode = | 
|  | new QpackInstructionOpcode{0b00000000, 0b11110000}; | 
|  | static const QpackInstruction* const instruction = | 
|  | new QpackInstruction{*opcode, | 
|  | {{QpackInstructionFieldType::kVarint, 3}, | 
|  | {QpackInstructionFieldType::kValue, 7}}}; | 
|  | return instruction; | 
|  | } | 
|  |  | 
|  | const QpackInstruction* QpackLiteralHeaderFieldInstruction() { | 
|  | static const QpackInstructionOpcode* const opcode = | 
|  | new QpackInstructionOpcode{0b00100000, 0b11100000}; | 
|  | static const QpackInstruction* const instruction = | 
|  | new QpackInstruction{*opcode, | 
|  | {{QpackInstructionFieldType::kName, 3}, | 
|  | {QpackInstructionFieldType::kValue, 7}}}; | 
|  | return instruction; | 
|  | } | 
|  |  | 
|  | const QpackLanguage* QpackRequestStreamLanguage() { | 
|  | static const QpackLanguage* const language = | 
|  | new QpackLanguage{QpackIndexedHeaderFieldInstruction(), | 
|  | QpackIndexedHeaderFieldPostBaseInstruction(), | 
|  | QpackLiteralHeaderFieldNameReferenceInstruction(), | 
|  | QpackLiteralHeaderFieldPostBaseInstruction(), | 
|  | QpackLiteralHeaderFieldInstruction()}; | 
|  | ValidateLangague(language); | 
|  | return language; | 
|  | } | 
|  |  | 
|  | // static | 
|  | QpackInstructionWithValues QpackInstructionWithValues::InsertWithNameReference( | 
|  | bool is_static, | 
|  | uint64_t name_index, | 
|  | quiche::QuicheStringPiece value) { | 
|  | QpackInstructionWithValues instruction_with_values; | 
|  | instruction_with_values.instruction_ = InsertWithNameReferenceInstruction(); | 
|  | instruction_with_values.s_bit_ = is_static; | 
|  | instruction_with_values.varint_ = name_index; | 
|  | instruction_with_values.value_ = value; | 
|  |  | 
|  | return instruction_with_values; | 
|  | } | 
|  |  | 
|  | // static | 
|  | QpackInstructionWithValues | 
|  | QpackInstructionWithValues::InsertWithoutNameReference( | 
|  | quiche::QuicheStringPiece name, | 
|  | quiche::QuicheStringPiece value) { | 
|  | QpackInstructionWithValues instruction_with_values; | 
|  | instruction_with_values.instruction_ = | 
|  | InsertWithoutNameReferenceInstruction(); | 
|  | instruction_with_values.name_ = name; | 
|  | instruction_with_values.value_ = value; | 
|  |  | 
|  | return instruction_with_values; | 
|  | } | 
|  |  | 
|  | // static | 
|  | QpackInstructionWithValues QpackInstructionWithValues::Duplicate( | 
|  | uint64_t index) { | 
|  | QpackInstructionWithValues instruction_with_values; | 
|  | instruction_with_values.instruction_ = DuplicateInstruction(); | 
|  | instruction_with_values.varint_ = index; | 
|  |  | 
|  | return instruction_with_values; | 
|  | } | 
|  |  | 
|  | // static | 
|  | QpackInstructionWithValues QpackInstructionWithValues::SetDynamicTableCapacity( | 
|  | uint64_t capacity) { | 
|  | QpackInstructionWithValues instruction_with_values; | 
|  | instruction_with_values.instruction_ = SetDynamicTableCapacityInstruction(); | 
|  | instruction_with_values.varint_ = capacity; | 
|  |  | 
|  | return instruction_with_values; | 
|  | } | 
|  |  | 
|  | // static | 
|  | QpackInstructionWithValues QpackInstructionWithValues::InsertCountIncrement( | 
|  | uint64_t increment) { | 
|  | QpackInstructionWithValues instruction_with_values; | 
|  | instruction_with_values.instruction_ = InsertCountIncrementInstruction(); | 
|  | instruction_with_values.varint_ = increment; | 
|  |  | 
|  | return instruction_with_values; | 
|  | } | 
|  |  | 
|  | // static | 
|  | QpackInstructionWithValues QpackInstructionWithValues::HeaderAcknowledgement( | 
|  | uint64_t stream_id) { | 
|  | QpackInstructionWithValues instruction_with_values; | 
|  | instruction_with_values.instruction_ = HeaderAcknowledgementInstruction(); | 
|  | instruction_with_values.varint_ = stream_id; | 
|  |  | 
|  | return instruction_with_values; | 
|  | } | 
|  |  | 
|  | // static | 
|  | QpackInstructionWithValues QpackInstructionWithValues::StreamCancellation( | 
|  | uint64_t stream_id) { | 
|  | QpackInstructionWithValues instruction_with_values; | 
|  | instruction_with_values.instruction_ = StreamCancellationInstruction(); | 
|  | instruction_with_values.varint_ = stream_id; | 
|  |  | 
|  | return instruction_with_values; | 
|  | } | 
|  |  | 
|  | // static | 
|  | QpackInstructionWithValues QpackInstructionWithValues::Prefix( | 
|  | uint64_t required_insert_count) { | 
|  | QpackInstructionWithValues instruction_with_values; | 
|  | instruction_with_values.instruction_ = QpackPrefixInstruction(); | 
|  | instruction_with_values.varint_ = required_insert_count; | 
|  | instruction_with_values.varint2_ = 0;    // Delta Base. | 
|  | instruction_with_values.s_bit_ = false;  // Delta Base sign. | 
|  |  | 
|  | return instruction_with_values; | 
|  | } | 
|  |  | 
|  | // static | 
|  | QpackInstructionWithValues QpackInstructionWithValues::IndexedHeaderField( | 
|  | bool is_static, | 
|  | uint64_t index) { | 
|  | QpackInstructionWithValues instruction_with_values; | 
|  | instruction_with_values.instruction_ = QpackIndexedHeaderFieldInstruction(); | 
|  | instruction_with_values.s_bit_ = is_static; | 
|  | instruction_with_values.varint_ = index; | 
|  |  | 
|  | return instruction_with_values; | 
|  | } | 
|  |  | 
|  | // static | 
|  | QpackInstructionWithValues | 
|  | QpackInstructionWithValues::LiteralHeaderFieldNameReference( | 
|  | bool is_static, | 
|  | uint64_t index, | 
|  | quiche::QuicheStringPiece value) { | 
|  | QpackInstructionWithValues instruction_with_values; | 
|  | instruction_with_values.instruction_ = | 
|  | QpackLiteralHeaderFieldNameReferenceInstruction(); | 
|  | instruction_with_values.s_bit_ = is_static; | 
|  | instruction_with_values.varint_ = index; | 
|  | instruction_with_values.value_ = value; | 
|  |  | 
|  | return instruction_with_values; | 
|  | } | 
|  |  | 
|  | // static | 
|  | QpackInstructionWithValues QpackInstructionWithValues::LiteralHeaderField( | 
|  | quiche::QuicheStringPiece name, | 
|  | quiche::QuicheStringPiece value) { | 
|  | QpackInstructionWithValues instruction_with_values; | 
|  | instruction_with_values.instruction_ = QpackLiteralHeaderFieldInstruction(); | 
|  | instruction_with_values.name_ = name; | 
|  | instruction_with_values.value_ = value; | 
|  |  | 
|  | return instruction_with_values; | 
|  | } | 
|  |  | 
|  | }  // namespace quic |