Project import generated by Copybara.

PiperOrigin-RevId: 237361882
Change-Id: I109a68f44db867b20f8c6a7732b0ce657133e52a
diff --git a/quic/core/qpack/qpack_instruction_decoder.cc b/quic/core/qpack/qpack_instruction_decoder.cc
new file mode 100644
index 0000000..dbb2076
--- /dev/null
+++ b/quic/core/qpack/qpack_instruction_decoder.cc
@@ -0,0 +1,310 @@
+// 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_decoder.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/logging.h"
+
+namespace quic {
+
+namespace {
+
+// Maximum length of header name and header value.  This limits the amount of
+// memory the peer can make the decoder allocate when sending string literals.
+const size_t kStringLiteralLengthLimit = 1024 * 1024;
+
+}  // namespace
+
+QpackInstructionDecoder::QpackInstructionDecoder(const QpackLanguage* language,
+                                                 Delegate* delegate)
+    : language_(language),
+      delegate_(delegate),
+      s_bit_(false),
+      varint_(0),
+      varint2_(0),
+      is_huffman_encoded_(false),
+      string_length_(0),
+      error_detected_(false),
+      state_(State::kStartInstruction) {}
+
+void QpackInstructionDecoder::Decode(QuicStringPiece data) {
+  DCHECK(!data.empty());
+  DCHECK(!error_detected_);
+
+  while (true) {
+    size_t bytes_consumed = 0;
+
+    switch (state_) {
+      case State::kStartInstruction:
+        DoStartInstruction(data);
+        break;
+      case State::kStartField:
+        DoStartField();
+        break;
+      case State::kReadBit:
+        DoReadBit(data);
+        break;
+      case State::kVarintStart:
+        bytes_consumed = DoVarintStart(data);
+        break;
+      case State::kVarintResume:
+        bytes_consumed = DoVarintResume(data);
+        break;
+      case State::kVarintDone:
+        DoVarintDone();
+        break;
+      case State::kReadString:
+        bytes_consumed = DoReadString(data);
+        break;
+      case State::kReadStringDone:
+        DoReadStringDone();
+        break;
+    }
+
+    if (error_detected_) {
+      return;
+    }
+
+    DCHECK_LE(bytes_consumed, data.size());
+
+    data = QuicStringPiece(data.data() + bytes_consumed,
+                           data.size() - bytes_consumed);
+
+    // Stop processing if no more data but next state would require it.
+    if (data.empty() && (state_ != State::kStartField) &&
+        (state_ != State::kVarintDone) && (state_ != State::kReadStringDone)) {
+      return;
+    }
+  }
+}
+
+bool QpackInstructionDecoder::AtInstructionBoundary() const {
+  return state_ == State::kStartInstruction;
+}
+
+void QpackInstructionDecoder::DoStartInstruction(QuicStringPiece data) {
+  DCHECK(!data.empty());
+
+  instruction_ = LookupOpcode(data[0]);
+  field_ = instruction_->fields.begin();
+
+  state_ = State::kStartField;
+}
+
+void QpackInstructionDecoder::DoStartField() {
+  if (field_ == instruction_->fields.end()) {
+    // Completed decoding this instruction.
+
+    if (!delegate_->OnInstructionDecoded(instruction_)) {
+      error_detected_ = true;
+      return;
+    }
+
+    state_ = State::kStartInstruction;
+    return;
+  }
+
+  switch (field_->type) {
+    case QpackInstructionFieldType::kSbit:
+    case QpackInstructionFieldType::kName:
+    case QpackInstructionFieldType::kValue:
+      state_ = State::kReadBit;
+      return;
+    case QpackInstructionFieldType::kVarint:
+    case QpackInstructionFieldType::kVarint2:
+      state_ = State::kVarintStart;
+      return;
+  }
+}
+
+void QpackInstructionDecoder::DoReadBit(QuicStringPiece data) {
+  DCHECK(!data.empty());
+
+  switch (field_->type) {
+    case QpackInstructionFieldType::kSbit: {
+      const uint8_t bitmask = field_->param;
+      s_bit_ = (data[0] & bitmask) == bitmask;
+
+      ++field_;
+      state_ = State::kStartField;
+
+      return;
+    }
+    case QpackInstructionFieldType::kName:
+    case QpackInstructionFieldType::kValue: {
+      const uint8_t prefix_length = field_->param;
+      DCHECK_GE(7, prefix_length);
+      const uint8_t bitmask = 1 << prefix_length;
+      is_huffman_encoded_ = (data[0] & bitmask) == bitmask;
+
+      state_ = State::kVarintStart;
+
+      return;
+    }
+    default:
+      DCHECK(false);
+  }
+}
+
+size_t QpackInstructionDecoder::DoVarintStart(QuicStringPiece data) {
+  DCHECK(!data.empty());
+  DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+         field_->type == QpackInstructionFieldType::kVarint2 ||
+         field_->type == QpackInstructionFieldType::kName ||
+         field_->type == QpackInstructionFieldType::kValue);
+
+  http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
+  http2::DecodeStatus status =
+      varint_decoder_.Start(data[0], field_->param, &buffer);
+
+  size_t bytes_consumed = 1 + buffer.Offset();
+  switch (status) {
+    case http2::DecodeStatus::kDecodeDone:
+      state_ = State::kVarintDone;
+      return bytes_consumed;
+    case http2::DecodeStatus::kDecodeInProgress:
+      state_ = State::kVarintResume;
+      return bytes_consumed;
+    case http2::DecodeStatus::kDecodeError:
+      OnError("Encoded integer too large.");
+      return bytes_consumed;
+  }
+}
+
+size_t QpackInstructionDecoder::DoVarintResume(QuicStringPiece data) {
+  DCHECK(!data.empty());
+  DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+         field_->type == QpackInstructionFieldType::kVarint2 ||
+         field_->type == QpackInstructionFieldType::kName ||
+         field_->type == QpackInstructionFieldType::kValue);
+
+  http2::DecodeBuffer buffer(data);
+  http2::DecodeStatus status = varint_decoder_.Resume(&buffer);
+
+  size_t bytes_consumed = buffer.Offset();
+  switch (status) {
+    case http2::DecodeStatus::kDecodeDone:
+      state_ = State::kVarintDone;
+      return bytes_consumed;
+    case http2::DecodeStatus::kDecodeInProgress:
+      DCHECK_EQ(bytes_consumed, data.size());
+      DCHECK(buffer.Empty());
+      return bytes_consumed;
+    case http2::DecodeStatus::kDecodeError:
+      OnError("Encoded integer too large.");
+      return bytes_consumed;
+  }
+}
+
+void QpackInstructionDecoder::DoVarintDone() {
+  DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+         field_->type == QpackInstructionFieldType::kVarint2 ||
+         field_->type == QpackInstructionFieldType::kName ||
+         field_->type == QpackInstructionFieldType::kValue);
+
+  if (field_->type == QpackInstructionFieldType::kVarint) {
+    varint_ = varint_decoder_.value();
+
+    ++field_;
+    state_ = State::kStartField;
+    return;
+  }
+
+  if (field_->type == QpackInstructionFieldType::kVarint2) {
+    varint2_ = varint_decoder_.value();
+
+    ++field_;
+    state_ = State::kStartField;
+    return;
+  }
+
+  string_length_ = varint_decoder_.value();
+  if (string_length_ > kStringLiteralLengthLimit) {
+    OnError("String literal too long.");
+    return;
+  }
+
+  QuicString* const string =
+      (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_;
+  string->clear();
+
+  if (string_length_ == 0) {
+    ++field_;
+    state_ = State::kStartField;
+    return;
+  }
+
+  string->reserve(string_length_);
+
+  state_ = State::kReadString;
+}
+
+size_t QpackInstructionDecoder::DoReadString(QuicStringPiece data) {
+  DCHECK(!data.empty());
+  DCHECK(field_->type == QpackInstructionFieldType::kName ||
+         field_->type == QpackInstructionFieldType::kValue);
+
+  QuicString* const string =
+      (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_;
+  DCHECK_LT(string->size(), string_length_);
+
+  size_t bytes_consumed =
+      std::min(string_length_ - string->size(), data.size());
+  string->append(data.data(), bytes_consumed);
+
+  DCHECK_LE(string->size(), string_length_);
+  if (string->size() == string_length_) {
+    state_ = State::kReadStringDone;
+  }
+  return bytes_consumed;
+}
+
+void QpackInstructionDecoder::DoReadStringDone() {
+  DCHECK(field_->type == QpackInstructionFieldType::kName ||
+         field_->type == QpackInstructionFieldType::kValue);
+
+  QuicString* const string =
+      (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_;
+  DCHECK_EQ(string->size(), string_length_);
+
+  if (is_huffman_encoded_) {
+    huffman_decoder_.Reset();
+    // HpackHuffmanDecoder::Decode() cannot perform in-place decoding.
+    QuicString decoded_value;
+    huffman_decoder_.Decode(*string, &decoded_value);
+    if (!huffman_decoder_.InputProperlyTerminated()) {
+      OnError("Error in Huffman-encoded string.");
+      return;
+    }
+    *string = std::move(decoded_value);
+  }
+
+  ++field_;
+  state_ = State::kStartField;
+}
+
+const QpackInstruction* QpackInstructionDecoder::LookupOpcode(
+    uint8_t byte) const {
+  for (const auto* instruction : *language_) {
+    if ((byte & instruction->opcode.mask) == instruction->opcode.value) {
+      return instruction;
+    }
+  }
+  // |language_| should be defined such that instruction opcodes cover every
+  // possible input.
+  DCHECK(false);
+  return nullptr;
+}
+
+void QpackInstructionDecoder::OnError(QuicStringPiece error_message) {
+  DCHECK(!error_detected_);
+
+  error_detected_ = true;
+  delegate_->OnError(error_message);
+}
+
+}  // namespace quic