// Copyright (c) 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.

#ifndef QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTIONS_H_
#define QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTIONS_H_

#include <cstdint>
#include <string>
#include <tuple>
#include <vector>

#include "absl/strings/string_view.h"
#include "quiche/quic/platform/api/quic_export.h"

namespace quic {

namespace test {
class QpackInstructionWithValuesPeer;
}  // namespace test

// Each instruction is identified with an opcode in the first byte.
// |mask| determines which bits are part of the opcode.
// |value| is the value of these bits.  (Other bits in value must be zero.)
struct QUICHE_EXPORT QpackInstructionOpcode {
  uint8_t value;
  uint8_t mask;
};

bool operator==(const QpackInstructionOpcode& a,
                const QpackInstructionOpcode& b);

// Possible types of an instruction field.  Decoding a static bit does not
// consume the current byte.  Decoding an integer or a length-prefixed string
// literal consumes all bytes containing the field value.
enum class QpackInstructionFieldType {
  // A single bit indicating whether the index refers to the static table, or
  // indicating the sign of Delta Base.  Called "S" bit because both "static"
  // and "sign" start with the letter "S".
  kSbit,
  // An integer encoded with variable length encoding.  This could be an index,
  // stream ID, maximum size, or Encoded Required Insert Count.
  kVarint,
  // A second integer encoded with variable length encoding.  This could be
  // Delta Base.
  kVarint2,
  // A header name or header value encoded as:
  //   a bit indicating whether it is Huffman encoded;
  //   the encoded length of the string;
  //   the header name or value optionally Huffman encoded.
  kName,
  kValue
};

// Each instruction field has a type and a parameter.
// The meaning of the parameter depends on the field type.
struct QUICHE_EXPORT QpackInstructionField {
  QpackInstructionFieldType type;
  // For a kSbit field, |param| is a mask with exactly one bit set.
  // For kVarint fields, |param| is the prefix length of the integer encoding.
  // For kName and kValue fields, |param| is the prefix length of the length of
  // the string, and the bit immediately preceding the prefix is interpreted as
  // the Huffman bit.
  uint8_t param;
};

using QpackInstructionFields = std::vector<QpackInstructionField>;

// A QPACK instruction consists of an opcode identifying the instruction,
// followed by a non-empty list of fields.  The last field must be integer or
// string literal type to guarantee that all bytes of the instruction are
// consumed.
struct QUICHE_EXPORT QpackInstruction {
  QpackInstruction(QpackInstructionOpcode opcode, QpackInstructionFields fields)
      : opcode(std::move(opcode)), fields(std::move(fields)) {}

  QpackInstruction(const QpackInstruction&) = delete;
  const QpackInstruction& operator=(const QpackInstruction&) = delete;

  QpackInstructionOpcode opcode;
  QpackInstructionFields fields;
};

// A language is a collection of instructions.  The order does not matter.
// Every possible input must match exactly one instruction.
using QpackLanguage = std::vector<const QpackInstruction*>;

// Wire format defined in
// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5

// 5.2 Encoder stream instructions

// 5.2.1 Insert With Name Reference
const QpackInstruction* InsertWithNameReferenceInstruction();

// 5.2.2 Insert Without Name Reference
const QpackInstruction* InsertWithoutNameReferenceInstruction();

// 5.2.3 Duplicate
const QpackInstruction* DuplicateInstruction();

// 5.2.4 Dynamic Table Size Update
const QpackInstruction* SetDynamicTableCapacityInstruction();

// Encoder stream language.
const QpackLanguage* QpackEncoderStreamLanguage();

// 5.3 Decoder stream instructions

// 5.3.1 Insert Count Increment
const QpackInstruction* InsertCountIncrementInstruction();

// 5.3.2 Header Acknowledgement
const QpackInstruction* HeaderAcknowledgementInstruction();

// 5.3.3 Stream Cancellation
const QpackInstruction* StreamCancellationInstruction();

// Decoder stream language.
const QpackLanguage* QpackDecoderStreamLanguage();

// 5.4.1. Header data prefix instructions

const QpackInstruction* QpackPrefixInstruction();

const QpackLanguage* QpackPrefixLanguage();

// 5.4.2. Request and push stream instructions

// 5.4.2.1. Indexed Header Field
const QpackInstruction* QpackIndexedHeaderFieldInstruction();

// 5.4.2.2. Indexed Header Field With Post-Base Index
const QpackInstruction* QpackIndexedHeaderFieldPostBaseInstruction();

// 5.4.2.3. Literal Header Field With Name Reference
const QpackInstruction* QpackLiteralHeaderFieldNameReferenceInstruction();

// 5.4.2.4. Literal Header Field With Post-Base Name Reference
const QpackInstruction* QpackLiteralHeaderFieldPostBaseInstruction();

// 5.4.2.5. Literal Header Field Without Name Reference
const QpackInstruction* QpackLiteralHeaderFieldInstruction();

// Request and push stream language.
const QpackLanguage* QpackRequestStreamLanguage();

// Storage for instruction and field values to be encoded.
// This class can only be instantiated using factory methods that take exactly
// the arguments that the corresponding instruction needs.
class QUICHE_EXPORT QpackInstructionWithValues {
 public:
  // 5.2 Encoder stream instructions
  static QpackInstructionWithValues InsertWithNameReference(
      bool is_static, uint64_t name_index, absl::string_view value);
  static QpackInstructionWithValues InsertWithoutNameReference(
      absl::string_view name, absl::string_view value);
  static QpackInstructionWithValues Duplicate(uint64_t index);
  static QpackInstructionWithValues SetDynamicTableCapacity(uint64_t capacity);

  // 5.3 Decoder stream instructions
  static QpackInstructionWithValues InsertCountIncrement(uint64_t increment);
  static QpackInstructionWithValues HeaderAcknowledgement(uint64_t stream_id);
  static QpackInstructionWithValues StreamCancellation(uint64_t stream_id);

  // 5.4.1. Header data prefix.  Delta Base is hardcoded to be zero.
  static QpackInstructionWithValues Prefix(uint64_t required_insert_count);

  // 5.4.2. Request and push stream instructions
  static QpackInstructionWithValues IndexedHeaderField(bool is_static,
                                                       uint64_t index);
  static QpackInstructionWithValues LiteralHeaderFieldNameReference(
      bool is_static, uint64_t index, absl::string_view value);
  static QpackInstructionWithValues LiteralHeaderField(absl::string_view name,
                                                       absl::string_view value);

  const QpackInstruction* instruction() const { return instruction_; }
  bool s_bit() const { return s_bit_; }
  uint64_t varint() const { return varint_; }
  uint64_t varint2() const { return varint2_; }
  absl::string_view name() const { return name_; }
  absl::string_view value() const { return value_; }

  // Used by QpackEncoder, because in the first pass it stores absolute indices,
  // which are converted into relative indices in the second pass after base is
  // determined.
  void set_varint(uint64_t varint) { varint_ = varint; }

 private:
  friend test::QpackInstructionWithValuesPeer;

  QpackInstructionWithValues() = default;

  // |*instruction| is not owned.
  const QpackInstruction* instruction_ = nullptr;
  bool s_bit_ = false;
  uint64_t varint_ = 0;
  uint64_t varint2_ = 0;
  absl::string_view name_;
  absl::string_view value_;
};

}  // namespace quic

#endif  // QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTIONS_H_
