// Copyright (c) 2012 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_QUIC_UTILS_H_
#define QUICHE_QUIC_CORE_QUIC_UTILS_H_

#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <optional>
#include <string>
#include <type_traits>

#include "absl/numeric/bits.h"
#include "absl/numeric/int128.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "quiche/quic/core/crypto/quic_random.h"
#include "quiche/quic/core/frames/quic_frame.h"
#include "quiche/quic/core/quic_connection_id.h"
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_versions.h"
#include "quiche/quic/platform/api/quic_export.h"
#include "quiche/quic/platform/api/quic_socket_address.h"
#include "quiche/common/platform/api/quiche_mem_slice.h"

namespace quic {

class QUICHE_EXPORT QuicUtils {
 public:
  QuicUtils() = delete;

  // Returns the 64 bit FNV1a hash of the data.  See
  // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param
  static uint64_t FNV1a_64_Hash(absl::string_view data);

  // Returns the 128 bit FNV1a hash of the data.  See
  // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param
  static absl::uint128 FNV1a_128_Hash(absl::string_view data);

  // Returns the 128 bit FNV1a hash of the two sequences of data.  See
  // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param
  static absl::uint128 FNV1a_128_Hash_Two(absl::string_view data1,
                                          absl::string_view data2);

  // Returns the 128 bit FNV1a hash of the three sequences of data.  See
  // http://www.isthe.com/chongo/tech/comp/fnv/index.html#FNV-param
  static absl::uint128 FNV1a_128_Hash_Three(absl::string_view data1,
                                            absl::string_view data2,
                                            absl::string_view data3);

  // SerializeUint128 writes the first 96 bits of |v| in little-endian form
  // to |out|.
  static void SerializeUint128Short(absl::uint128 v, uint8_t* out);

  // Returns AddressChangeType as a string.
  static std::string AddressChangeTypeToString(AddressChangeType type);

  // Returns SentPacketState as a char*.
  static const char* SentPacketStateToString(SentPacketState state);

  // Returns QuicLongHeaderType as a char*.
  static const char* QuicLongHeaderTypetoString(QuicLongHeaderType type);

  // Returns AckResult as a char*.
  static const char* AckResultToString(AckResult result);

  // Determines and returns change type of address change from |old_address| to
  // |new_address|.
  static AddressChangeType DetermineAddressChangeType(
      const QuicSocketAddress& old_address,
      const QuicSocketAddress& new_address);

  // Returns the opposite Perspective of the |perspective| passed in.
  static constexpr Perspective InvertPerspective(Perspective perspective) {
    return perspective == Perspective::IS_CLIENT ? Perspective::IS_SERVER
                                                 : Perspective::IS_CLIENT;
  }

  // Returns true if a packet is ackable. A packet is unackable if it can never
  // be acked. Occurs when a packet is never sent, after it is acknowledged
  // once, or if it's a crypto packet we never expect to receive an ack for.
  static bool IsAckable(SentPacketState state);

  // Returns true if frame with |type| is retransmittable. A retransmittable
  // frame should be retransmitted if it is detected as lost.
  static bool IsRetransmittableFrame(QuicFrameType type);

  // Returns true if |frame| is a handshake frame in version |version|.
  static bool IsHandshakeFrame(const QuicFrame& frame,
                               QuicTransportVersion transport_version);

  // Return true if any frame in |frames| is of |type|.
  static bool ContainsFrameType(const QuicFrames& frames, QuicFrameType type);

  // Returns packet state corresponding to |retransmission_type|.
  static SentPacketState RetransmissionTypeToPacketState(
      TransmissionType retransmission_type);

  // Returns true if header with |first_byte| is considered as an IETF QUIC
  // packet header. This only works on the server.
  static bool IsIetfPacketHeader(uint8_t first_byte);

  // Returns true if header with |first_byte| is considered as an IETF QUIC
  // short packet header.
  static bool IsIetfPacketShortHeader(uint8_t first_byte);

  // Returns ID to denote an invalid stream of |version|.
  static QuicStreamId GetInvalidStreamId(QuicTransportVersion version);

  // Returns crypto stream ID of |version|.
  static QuicStreamId GetCryptoStreamId(QuicTransportVersion version);

  // Returns whether |id| is the stream ID for the crypto stream. If |version|
  // is a version where crypto data doesn't go over stream frames, this function
  // will always return false.
  static bool IsCryptoStreamId(QuicTransportVersion version, QuicStreamId id);

  // Returns headers stream ID of |version|.
  static QuicStreamId GetHeadersStreamId(QuicTransportVersion version);

  // Returns true if |id| is considered as client initiated stream ID.
  static bool IsClientInitiatedStreamId(QuicTransportVersion version,
                                        QuicStreamId id);

  // Returns true if |id| is considered as server initiated stream ID.
  static bool IsServerInitiatedStreamId(QuicTransportVersion version,
                                        QuicStreamId id);

  // Returns true if the stream ID represents a stream initiated by the
  // provided perspective.
  static bool IsOutgoingStreamId(ParsedQuicVersion version, QuicStreamId id,
                                 Perspective perspective);

  // Returns true if |id| is considered as bidirectional stream ID. Only used in
  // v99.
  static bool IsBidirectionalStreamId(QuicStreamId id,
                                      ParsedQuicVersion version);

  // Returns stream type.  Either |perspective| or |peer_initiated| would be
  // enough together with |id|.  This method enforces that the three parameters
  // are consistent.  Only used in v99.
  static StreamType GetStreamType(QuicStreamId id, Perspective perspective,
                                  bool peer_initiated,
                                  ParsedQuicVersion version);

  // Returns the delta between consecutive stream IDs of the same type.
  static QuicStreamId StreamIdDelta(QuicTransportVersion version);

  // Returns the first initiated bidirectional stream ID of |perspective|.
  static QuicStreamId GetFirstBidirectionalStreamId(
      QuicTransportVersion version, Perspective perspective);

  // Returns the first initiated unidirectional stream ID of |perspective|.
  static QuicStreamId GetFirstUnidirectionalStreamId(
      QuicTransportVersion version, Perspective perspective);

  // Returns the largest possible client initiated bidirectional stream ID.
  static QuicStreamId GetMaxClientInitiatedBidirectionalStreamId(
      QuicTransportVersion version);

  // Generates a random 64bit connection ID.
  static QuicConnectionId CreateRandomConnectionId();

  // Generates a random 64bit connection ID using the provided QuicRandom.
  static QuicConnectionId CreateRandomConnectionId(QuicRandom* random);

  // Generates a random connection ID of the given length.
  static QuicConnectionId CreateRandomConnectionId(
      uint8_t connection_id_length);

  // Generates a random connection ID of the given length using the provided
  // QuicRandom.
  static QuicConnectionId CreateRandomConnectionId(uint8_t connection_id_length,
                                                   QuicRandom* random);

  // Returns true if the connection ID length is valid for this QUIC version.
  static bool IsConnectionIdLengthValidForVersion(
      size_t connection_id_length, QuicTransportVersion transport_version);

  // Returns true if the connection ID is valid for this QUIC version.
  static bool IsConnectionIdValidForVersion(
      QuicConnectionId connection_id, QuicTransportVersion transport_version);

  // Returns a connection ID suitable for QUIC use-cases that do not need the
  // connection ID for multiplexing. If the version allows variable lengths,
  // a connection of length zero is returned, otherwise 64bits set to zero.
  static QuicConnectionId CreateZeroConnectionId(QuicTransportVersion version);

  // Generates a 128bit stateless reset token based on a connection ID.
  static StatelessResetToken GenerateStatelessResetToken(
      QuicConnectionId connection_id);

  // Determines packet number space from |encryption_level|.
  static PacketNumberSpace GetPacketNumberSpace(
      EncryptionLevel encryption_level);

  // Determines encryption level to send ACK in |packet_number_space|.
  static EncryptionLevel GetEncryptionLevelToSendAckofSpace(
      PacketNumberSpace packet_number_space);

  // Get the maximum value for a V99/IETF QUIC stream count. If a count
  // exceeds this value, it will result in a stream ID that exceeds the
  // implementation limit on stream ID size.
  static QuicStreamCount GetMaxStreamCount();

  // Return true if this frame is an IETF probing frame.
  static bool IsProbingFrame(QuicFrameType type);

  // Return true if the two stateless reset tokens are equal. Performs the
  // comparison in constant time.
  static bool AreStatelessResetTokensEqual(const StatelessResetToken& token1,
                                           const StatelessResetToken& token2);

  // Return ture if this frame is an ack-eliciting frame.
  static bool IsAckElicitingFrame(QuicFrameType type);
};

// Returns true if the specific ID is a valid WebTransport session ID that our
// implementation can process.
bool IsValidWebTransportSessionId(WebTransportSessionId id,
                                  ParsedQuicVersion transport_version);

QuicByteCount MemSliceSpanTotalSize(absl::Span<quiche::QuicheMemSlice> span);

// Computes a SHA-256 hash and returns the raw bytes of the hash.
QUICHE_EXPORT std::string RawSha256(absl::string_view input);

// BitMask<Index, Mask> is a set of elements of type `Index` represented as a
// bitmask of an underlying integer type `Mask` (uint64_t by default). The
// underlying type has to be large enough to fit all possible values of `Index`.
template <typename Index, typename Mask = uint64_t>
class QUICHE_EXPORT BitMask {
 public:
  explicit constexpr BitMask(std::initializer_list<Index> bits) {
    for (Index bit : bits) {
      mask_ |= MakeMask(bit);
    }
  }

  BitMask() = default;
  BitMask(const BitMask& other) = default;
  BitMask& operator=(const BitMask& other) = default;

  constexpr void Set(Index bit) { mask_ |= MakeMask(bit); }

  constexpr void Set(std::initializer_list<Index> bits) {
    mask_ |= BitMask(bits).mask();
  }

  constexpr bool IsSet(Index bit) const { return (MakeMask(bit) & mask_) != 0; }

  constexpr void ClearAll() { mask_ = 0; }

  // Returns true if any of the bits is set.
  bool Any() const { return mask_ != 0; }

  // Returns the highest bit set, or nullopt if the mask is all zeroes.
  std::optional<Index> Max() const {
    if (!Any()) {
      return std::nullopt;
    }
    return static_cast<Index>(NumBits() - absl::countl_zero(mask_) - 1);
  }

  static constexpr size_t NumBits() { return 8 * sizeof(Mask); }

  friend bool operator==(const BitMask& lhs, const BitMask& rhs) {
    return lhs.mask_ == rhs.mask_;
  }

  // Bitwise AND that can act as a set intersection between two bit masks.
  BitMask<Index, Mask> operator&(const BitMask<Index, Mask>& rhs) const {
    return BitMask<Index, Mask>(mask_ & rhs.mask_);
  }

  std::string DebugString() const {
    return absl::StrCat("0x", absl::Hex(mask_));
  }

  constexpr Mask mask() const { return mask_; }

 private:
  explicit constexpr BitMask(Mask mask) : mask_(mask) {}

  template <typename Bit>
  static constexpr std::enable_if_t<std::is_enum_v<Bit>, Mask> MakeMask(
      Bit bit) {
    using IntType = typename std::underlying_type<Bit>::type;
    return MakeMask(static_cast<IntType>(bit));
  }

  template <typename Bit>
  static constexpr std::enable_if_t<!std::is_enum_v<Bit>, Mask> MakeMask(
      Bit bit) {
    // We can't use QUICHE_DCHECK_LT here, since it doesn't work with constexpr.
    QUICHE_DCHECK(bit < static_cast<Bit>(NumBits()));
    if constexpr (std::is_signed_v<Bit>) {
      QUICHE_DCHECK(bit >= 0);
    }
    return Mask(1) << bit;
  }

  Mask mask_ = 0;
};

// Ensure that the BitMask constructor can be evaluated as constexpr.
static_assert(BitMask<int>({1, 2, 3}).mask() == 0x0e);

}  // namespace quic

#endif  // QUICHE_QUIC_CORE_QUIC_UTILS_H_
