text: /*++
  
      Copyright (c) Microsoft Corporation.
      Licensed under the MIT License.
  
  Abstract:
  
      Functions for encoding and decoding frames.
  
  --*/
  
  #include "precomp.h"
  
  _Post_equal_to_(Buffer + sizeof(uint8_t))
  uint8_t*
  QuicUint8Encode(
      _In_ uint8_t Value,
      _Out_ uint8_t* Buffer
      )
  {
      *Buffer = Value;
      return Buffer + sizeof(uint8_t);
  }
  
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicUint8tDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_
      _Deref_in_range_(0, BufferLength)
      _Deref_out_range_(0, BufferLength)
          uint16_t* Offset,
      _Out_ uint8_t* Value
      )
  {
      if (BufferLength < sizeof(uint8_t) + *Offset) {
          return FALSE;
      }
      *Value = Buffer[*Offset];
      *Offset += sizeof(uint8_t);
      return TRUE;
  }
  
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicAckHeaderEncode(
      _In_ const QUIC_ACK_EX * const Frame,
      _In_opt_ QUIC_ACK_ECN_EX* Ecn,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          QuicVarIntSize(Frame->LargestAcknowledged) +
          QuicVarIntSize(Frame->AckDelay) +
          QuicVarIntSize(Frame->AdditionalAckBlockCount) +
          QuicVarIntSize(Frame->FirstAckBlock);
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicUint8Encode(Ecn == NULL ? QUIC_FRAME_ACK : QUIC_FRAME_ACK + 1, Buffer);
      Buffer = QuicVarIntEncode(Frame->LargestAcknowledged, Buffer);
      Buffer = QuicVarIntEncode(Frame->AckDelay, Buffer);
      Buffer = QuicVarIntEncode(Frame->AdditionalAckBlockCount, Buffer);
      QuicVarIntEncode(Frame->FirstAckBlock, Buffer);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicAckHeaderDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_ACK_EX* Frame
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->LargestAcknowledged) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->AckDelay) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->AdditionalAckBlockCount) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->FirstAckBlock) ||
          Frame->FirstAckBlock > Frame->LargestAcknowledged) {
          return FALSE;
      }
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicAckBlockEncode(
      _In_ const QUIC_ACK_BLOCK_EX * const Block,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          QuicVarIntSize(Block->Gap) +
          QuicVarIntSize(Block->AckBlock);
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicVarIntEncode(Block->Gap, Buffer);
      QuicVarIntEncode(Block->AckBlock, Buffer);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicAckBlockDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_ACK_BLOCK_EX* Block
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Block->Gap) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Block->AckBlock)) {
          return FALSE;
      }
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicAckEcnEncode(
      _In_ const QUIC_ACK_ECN_EX * const Ecn,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          QuicVarIntSize(Ecn->ECT_0_Count) +
          QuicVarIntSize(Ecn->ECT_1_Count) +
          QuicVarIntSize(Ecn->CE_Count);
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicVarIntEncode(Ecn->ECT_0_Count, Buffer);
      Buffer = QuicVarIntEncode(Ecn->ECT_1_Count, Buffer);
      QuicVarIntEncode(Ecn->CE_Count, Buffer);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicAckEcnDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_ACK_ECN_EX* Ecn
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Ecn->ECT_0_Count) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Ecn->ECT_1_Count) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Ecn->CE_Count)) {
          return FALSE;
      }
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicAckFrameEncode(
      _In_ const QUIC_RANGE * const AckBlocks,
      _In_ uint64_t AckDelay,
      _In_opt_ QUIC_ACK_ECN_EX* Ecn,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint32_t i = QuicRangeSize(AckBlocks) - 1;
  
      QUIC_SUBRANGE* LastSub = QuicRangeGet(AckBlocks, i);
      uint64_t Largest = QuicRangeGetHigh(LastSub);
      uint64_t Count = LastSub->Count;
  
      //
      // Write the ACK Frame Header
      //
      QUIC_ACK_EX Frame = {
          Largest,                // LargestAcknowledged
          AckDelay,               // AckDelay
          i,                      // AdditionalAckBlockCount
          Count - 1               // FirstAckBlock
      };
  
      if (!QuicAckHeaderEncode(&Frame, Ecn, Offset, BufferLength, Buffer)) {
          return FALSE;
      }
  
      //
      // Write any additional ACK Blocks
      //
      while (i != 0) {
  
          QUIC_DBG_ASSERT(Largest >= Count);
          Largest -= Count;
  
          QUIC_SUBRANGE* Next = QuicRangeGet(AckBlocks, i - 1);
          uint64_t NextLargest = QuicRangeGetHigh(Next);
          Count = Next->Count;
  
          QUIC_DBG_ASSERT(Largest > NextLargest);
          QUIC_DBG_ASSERT(Count > 0);
  
          QUIC_ACK_BLOCK_EX Block = {
              (Largest - NextLargest) - 1,    // Gap
              Count - 1                       // AckBlock
          };
  
          if (!QuicAckBlockEncode(&Block, Offset, BufferLength, Buffer)) {
              QUIC_TEL_ASSERT(FALSE); // TODO - Support partial ACK array encoding by updating the 'AdditionalAckBlockCount' field.
              return FALSE;
          }
  
          Largest = NextLargest;
          i--;
      }
  
      if (Ecn != NULL) {
          if (!QuicAckEcnEncode(Ecn, Offset, BufferLength, Buffer)) {
              return FALSE;
          }
      }
  
      return TRUE;
  }
  
  //
  // Given that the max UDP packet is 64k, this is a reasonable upper bound for
  // the number of ACK blocks possible.
  //
  #define QUIC_MAX_NUMBER_ACK_BLOCKS 0x10000
  
  //
  // Decodes the ACK_FRAME (has packet numbers from largest to smallest) to a
  // QUIC_RANGE format (smallest to largest).
  //
  _Success_(return != FALSE)
  BOOLEAN
  QuicAckFrameDecode(
      _In_ QUIC_FRAME_TYPE FrameType,
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ BOOLEAN* InvalidFrame,
      _Inout_ QUIC_RANGE* AckRanges, // Pre-Initialized by caller
      _When_(FrameType == QUIC_FRAME_ACK_1, _Out_)
          QUIC_ACK_ECN_EX* Ecn,
      _Out_ uint64_t* AckDelay
      )
  {
      *InvalidFrame = FALSE;
      QUIC_DBG_ASSERT(AckRanges->SubRanges); // Should be pre-initialized.
  
      //
      // Decode the ACK frame header.
      //
      QUIC_ACK_EX Frame;
      if (!QuicAckHeaderDecode(BufferLength, Buffer, Offset, &Frame)) {
          *InvalidFrame = TRUE;
          return FALSE;
      }
  
      //
      // Insert the largest/first block into the range.
      //
  
      uint64_t Largest = Frame.LargestAcknowledged;
      uint64_t Count = Frame.FirstAckBlock + 1;
  
      BOOLEAN DontCare;
      if (!QuicRangeAddRange(AckRanges, Largest + 1 - Count, Count, &DontCare)) {
          return FALSE;
      }
  
      if (Frame.AdditionalAckBlockCount >= QUIC_MAX_NUMBER_ACK_BLOCKS) {
          *InvalidFrame = TRUE;
          return FALSE;
      }
  
      //
      // Insert all the rest of the blocks (if any) into the range.
      //
  
      for (uint32_t i = 0; i < (uint32_t)Frame.AdditionalAckBlockCount; i++) {
  
          if (Count > Largest) {
              *InvalidFrame = TRUE;
              return FALSE;
          }
  
          Largest -= Count;
  
          QUIC_ACK_BLOCK_EX Block;
          if (!QuicAckBlockDecode(BufferLength, Buffer, Offset, &Block)) {
              *InvalidFrame = TRUE;
              return FALSE;
          }
  
          if (Block.Gap + 1 > Largest) {
              *InvalidFrame = TRUE;
              return FALSE;
          }
  
          Largest -= (Block.Gap + 1);
          Count = Block.AckBlock + 1;
  
          //
          // N.B. The efficiency here isn't great because we are always inserting
          // values less than the current minimum, which requires a complete
          // memmove of the current array. A circular buffer that allows both
          // forward and backward growth would fix this.
          //
  
          if (!QuicRangeAddRange(AckRanges, Largest - Count + 1, Count, &DontCare)) {
              return FALSE;
          }
      }
  
      *AckDelay = Frame.AckDelay;
  
      if (FrameType == QUIC_FRAME_ACK_1) {
          //
          // The ECN section was provided. Decode it as well.
          //
          if (!QuicAckEcnDecode(BufferLength, Buffer, Offset, Ecn)) {
              return FALSE;
          }
      }
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicResetStreamFrameEncode(
      _In_ const QUIC_RESET_STREAM_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          QuicVarIntSize(Frame->ErrorCode) +
          QuicVarIntSize(Frame->StreamID) +
          QuicVarIntSize(Frame->FinalSize);
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicUint8Encode(QUIC_FRAME_RESET_STREAM, Buffer);
      Buffer = QuicVarIntEncode(Frame->StreamID, Buffer);
      Buffer = QuicVarIntEncode(Frame->ErrorCode, Buffer);
      QuicVarIntEncode(Frame->FinalSize, Buffer);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicResetStreamFrameDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_RESET_STREAM_EX* Frame
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->StreamID) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->ErrorCode) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->FinalSize)) {
          return FALSE;
      }
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicStopSendingFrameEncode(
      _In_ const QUIC_STOP_SENDING_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          QuicVarIntSize(Frame->StreamID) +
          QuicVarIntSize(Frame->ErrorCode);
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicUint8Encode(QUIC_FRAME_STOP_SENDING, Buffer);
      Buffer = QuicVarIntEncode(Frame->StreamID, Buffer);
      QuicVarIntEncode(Frame->ErrorCode, Buffer);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicStopSendingFrameDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_STOP_SENDING_EX* Frame
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->StreamID) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->ErrorCode)) {
          return FALSE;
      }
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicCryptoFrameEncode(
      _In_ const QUIC_CRYPTO_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      QUIC_DBG_ASSERT(Frame->Length < UINT16_MAX);
  
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          QuicVarIntSize(Frame->Offset) +
          QuicVarIntSize(Frame->Length) +
          (uint16_t)Frame->Length;
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicUint8Encode(QUIC_FRAME_CRYPTO, Buffer);
      Buffer = QuicVarIntEncode(Frame->Offset, Buffer);
      Buffer = QuicVarIntEncode(Frame->Length, Buffer);
      QuicCopyMemory(Buffer, Frame->Data, (uint16_t)Frame->Length);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicCryptoFrameDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_CRYPTO_EX* Frame
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->Offset) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->Length) ||
          BufferLength < Frame->Length + *Offset) {
          return FALSE;
      }
      Frame->Data = Buffer + *Offset;
      *Offset += (uint16_t)Frame->Length;
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicNewTokenFrameEncode(
      _In_ const QUIC_NEW_TOKEN_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          QuicVarIntSize(Frame->TokenLength) +
          (uint16_t)Frame->TokenLength;
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicUint8Encode(QUIC_FRAME_NEW_TOKEN, Buffer);
      Buffer = QuicVarIntEncode(Frame->TokenLength, Buffer);
      QuicCopyMemory(Buffer, Frame->Token, (uint16_t)Frame->TokenLength);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicNewTokenFrameDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_NEW_TOKEN_EX* Frame
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->TokenLength) ||
          BufferLength < Frame->TokenLength + *Offset) {
          return FALSE;
      }
  
      Frame->Token = Buffer + *Offset;
      *Offset += (uint16_t)Frame->TokenLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicStreamFrameEncode(
      _In_ const QUIC_STREAM_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      __analysis_assume(Frame->Length < 0x10000);
      QUIC_DBG_ASSERT(Frame->Length < 0x10000);
  
      uint16_t RequiredLength =
          QuicStreamFrameHeaderSize(Frame) +
          (uint16_t)Frame->Length;
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      QUIC_STREAM_FRAME_TYPE Type = {{{
          Frame->Fin,
          Frame->ExplicitLength,
          Frame->Offset != 0 ? TRUE : FALSE,
          0b00001
      }}};
  
      Buffer = Buffer + *Offset;
      Buffer = QuicUint8Encode(Type.Type, Buffer);
      Buffer = QuicVarIntEncode(Frame->StreamID, Buffer);
      if (Type.OFF) {
          Buffer = QuicVarIntEncode(Frame->Offset, Buffer);
      }
      if (Type.LEN) {
          Buffer = QuicVarIntEncode2Bytes(Frame->Length, Buffer); // We always use two bytes for the explicit length.
      }
      QUIC_DBG_ASSERT(Frame->Length == 0 || Buffer == Frame->Data); // Caller already set the data.
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicStreamFrameDecode(
      _In_ QUIC_FRAME_TYPE FrameType,
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_
      _Deref_in_range_(0, BufferLength)
      _Deref_out_range_(0, BufferLength)
          uint16_t* Offset,
      _Out_ QUIC_STREAM_EX* Frame
      )
  {
      QUIC_STREAM_FRAME_TYPE Type = { .Type = FrameType };
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->StreamID)) {
          return FALSE;
      }
      if (Type.OFF) {
          if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->Offset)) {
              return FALSE;
          }
      } else {
          Frame->Offset = 0;
      }
      if (Type.LEN) {
          if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->Length) ||
              BufferLength < Frame->Length + *Offset) {
              return FALSE;
          }
          Frame->ExplicitLength = TRUE;
      } else {
          QUIC_ANALYSIS_ASSERT(BufferLength >= *Offset);
          Frame->Length = BufferLength - *Offset;
      }
      Frame->Fin = Type.FIN;
      Frame->Data = Buffer + *Offset;
      *Offset += (uint16_t)Frame->Length;
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicMaxDataFrameEncode(
      _In_ const QUIC_MAX_DATA_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          QuicVarIntSize(Frame->MaximumData);
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicUint8Encode(QUIC_FRAME_MAX_DATA, Buffer);
      QuicVarIntEncode(Frame->MaximumData, Buffer);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicMaxDataFrameDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_MAX_DATA_EX* Frame
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->MaximumData)) {
          return FALSE;
      }
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicMaxStreamDataFrameEncode(
      _In_ const QUIC_MAX_STREAM_DATA_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          QuicVarIntSize(Frame->StreamID) +
          QuicVarIntSize(Frame->MaximumData);
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicUint8Encode(QUIC_FRAME_MAX_STREAM_DATA, Buffer);
      Buffer = QuicVarIntEncode(Frame->StreamID, Buffer);
      QuicVarIntEncode(Frame->MaximumData, Buffer);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicMaxStreamDataFrameDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_MAX_STREAM_DATA_EX* Frame
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->StreamID) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->MaximumData)) {
          return FALSE;
      }
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicMaxStreamsFrameEncode(
      _In_ const QUIC_MAX_STREAMS_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          QuicVarIntSize(Frame->MaximumStreams);
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer =
          QuicUint8Encode(
              Frame->BidirectionalStreams ?
                  QUIC_FRAME_MAX_STREAMS :
                  QUIC_FRAME_MAX_STREAMS_1,
              Buffer);
      QuicVarIntEncode(Frame->MaximumStreams, Buffer);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicMaxStreamsFrameDecode(
      _In_ QUIC_FRAME_TYPE FrameType,
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_MAX_STREAMS_EX* Frame
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->MaximumStreams)) {
          return FALSE;
      }
      Frame->BidirectionalStreams = FrameType == QUIC_FRAME_MAX_STREAMS;
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicDataBlockedFrameEncode(
      _In_ const QUIC_DATA_BLOCKED_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          QuicVarIntSize(Frame->DataLimit);
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicUint8Encode(QUIC_FRAME_DATA_BLOCKED, Buffer);
      QuicVarIntEncode(Frame->DataLimit, Buffer);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicDataBlockedFrameDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_DATA_BLOCKED_EX* Frame
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->DataLimit)) {
          return FALSE;
      }
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicStreamDataBlockedFrameEncode(
      _In_ const QUIC_STREAM_DATA_BLOCKED_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          QuicVarIntSize(Frame->StreamID) +
          QuicVarIntSize(Frame->StreamDataLimit);
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicUint8Encode(QUIC_FRAME_STREAM_DATA_BLOCKED, Buffer);
      Buffer = QuicVarIntEncode(Frame->StreamID, Buffer);
      QuicVarIntEncode(Frame->StreamDataLimit, Buffer);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicStreamDataBlockedFrameDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_STREAM_DATA_BLOCKED_EX* Frame
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->StreamID) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->StreamDataLimit)) {
          return FALSE;
      }
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicStreamsBlockedFrameEncode(
      _In_ const QUIC_STREAMS_BLOCKED_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          QuicVarIntSize(Frame->StreamLimit);
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer =
          QuicUint8Encode(
              Frame->BidirectionalStreams ?
                  QUIC_FRAME_STREAMS_BLOCKED :
                  QUIC_FRAME_STREAMS_BLOCKED_1,
              Buffer);
      QuicVarIntEncode(Frame->StreamLimit, Buffer);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicStreamsBlockedFrameDecode(
      _In_ QUIC_FRAME_TYPE FrameType,
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_STREAMS_BLOCKED_EX* Frame
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->StreamLimit)) {
          return FALSE;
      }
      Frame->BidirectionalStreams = FrameType == QUIC_FRAME_STREAMS_BLOCKED;
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicNewConnectionIDFrameEncode(
      _In_ const QUIC_NEW_CONNECTION_ID_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          QuicVarIntSize(Frame->Sequence) +
          QuicVarIntSize(Frame->RetirePriorTo) +
          sizeof(uint8_t) +     // Length
          Frame->Length +
          QUIC_STATELESS_RESET_TOKEN_LENGTH;
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicUint8Encode(QUIC_FRAME_NEW_CONNECTION_ID, Buffer);
      Buffer = QuicVarIntEncode(Frame->Sequence, Buffer);
      Buffer = QuicVarIntEncode(Frame->RetirePriorTo, Buffer);
      Buffer = QuicUint8Encode(Frame->Length, Buffer);
      QuicCopyMemory(Buffer, Frame->Buffer, Frame->Length + QUIC_STATELESS_RESET_TOKEN_LENGTH);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicNewConnectionIDFrameDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_NEW_CONNECTION_ID_EX* Frame
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->Sequence) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->RetirePriorTo) ||
          Frame->RetirePriorTo > Frame->Sequence ||
          BufferLength < *Offset + 1) {
          return FALSE;
      }
  
      Frame->Length = Buffer[(*Offset)++];
  
      if (Frame->Length < 1 || Frame->Length > QUIC_MAX_CONNECTION_ID_LENGTH_V1 ||
          BufferLength < *Offset + Frame->Length + QUIC_STATELESS_RESET_TOKEN_LENGTH) {
          return FALSE;
      }
  
      QuicCopyMemory(Frame->Buffer, Buffer + *Offset, Frame->Length + QUIC_STATELESS_RESET_TOKEN_LENGTH);
      *Offset += Frame->Length + QUIC_STATELESS_RESET_TOKEN_LENGTH;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicRetireConnectionIDFrameEncode(
      _In_ const QUIC_RETIRE_CONNECTION_ID_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          QuicVarIntSize(Frame->Sequence);
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicUint8Encode(QUIC_FRAME_RETIRE_CONNECTION_ID, Buffer);
      QuicVarIntEncode(Frame->Sequence, Buffer);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicRetireConnectionIDFrameDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_RETIRE_CONNECTION_ID_EX* Frame
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->Sequence)) {
          return FALSE;
      }
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicPathChallengeFrameEncode(
      _In_ QUIC_FRAME_TYPE FrameType,
      _In_ const QUIC_PATH_CHALLENGE_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          sizeof(Frame->Data);
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicUint8Encode(FrameType, Buffer);
      QuicCopyMemory(Buffer, Frame->Data, sizeof(Frame->Data));
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicPathChallengeFrameDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_PATH_CHALLENGE_EX* Frame
      )
  {
      if (BufferLength < *Offset + sizeof(Frame->Data)) {
          return FALSE;
      }
      QuicCopyMemory(Frame->Data, Buffer + *Offset, sizeof(Frame->Data));
      *Offset += sizeof(Frame->Data);
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicConnCloseFrameEncode(
      _In_ const QUIC_CONNECTION_CLOSE_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          QuicVarIntSize(Frame->ErrorCode) +
          (Frame->ApplicationClosed ? 0 : QuicVarIntSize(Frame->FrameType)) +
          QuicVarIntSize(Frame->ReasonPhraseLength) +
          (uint16_t)Frame->ReasonPhraseLength;
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer =
          QuicUint8Encode(
              Frame->ApplicationClosed ?
                  QUIC_FRAME_CONNECTION_CLOSE_1 :
                  QUIC_FRAME_CONNECTION_CLOSE,
              Buffer);
      Buffer = QuicVarIntEncode(Frame->ErrorCode, Buffer);
      if (!Frame->ApplicationClosed) {
          Buffer = QuicVarIntEncode(Frame->FrameType, Buffer);
      }
      Buffer = QuicVarIntEncode(Frame->ReasonPhraseLength, Buffer);
      if (Frame->ReasonPhraseLength != 0) {
          QuicCopyMemory(Buffer, Frame->ReasonPhrase, (size_t)Frame->ReasonPhraseLength);
      }
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicConnCloseFrameDecode(
      _In_ QUIC_FRAME_TYPE FrameType,
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_CONNECTION_CLOSE_EX* Frame
      )
  {
      Frame->ApplicationClosed = FrameType == QUIC_FRAME_CONNECTION_CLOSE_1;
      Frame->FrameType = 0; // Default to make OACR happy.
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->ErrorCode) ||
          (!Frame->ApplicationClosed &&
           !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->FrameType)) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->ReasonPhraseLength) ||
          (uint64_t)BufferLength < *Offset + Frame->ReasonPhraseLength) {
          return FALSE;
      }
      Frame->ReasonPhrase = (char*)(Buffer + *Offset);
      *Offset += (uint16_t)Frame->ReasonPhraseLength;
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicDatagramFrameEncodeEx(
      _In_reads_(BufferCount)
          const QUIC_BUFFER* const Buffers,
      _In_ uint32_t BufferCount,
      _In_ uint64_t TotalLength,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset)
          uint8_t* Buffer
      )
  {
      QUIC_DATAGRAM_FRAME_TYPE Type = {{{
          TRUE,
          0b0011000
      }}};
  
      uint16_t RequiredLength =
          sizeof(uint8_t) +     // Type
          (Type.LEN ? QuicVarIntSize(TotalLength) : 0) +
          (uint16_t)TotalLength;
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      Buffer = Buffer + *Offset;
      Buffer = QuicUint8Encode(Type.Type, Buffer);
      if (Type.LEN) {
          Buffer = QuicVarIntEncode(TotalLength, Buffer);
      }
      for (uint32_t i = 0; i < BufferCount; ++i) {
          if (Buffers[i].Length != 0) {
              QuicCopyMemory(Buffer, Buffers[i].Buffer, Buffers[i].Length);
              Buffer += Buffers[i].Length;
          }
      }
  
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicDatagramFrameDecode(
      _In_ QUIC_FRAME_TYPE FrameType,
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Deref_in_range_(0, BufferLength)
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_DATAGRAM_EX* Frame
      )
  {
      QUIC_DATAGRAM_FRAME_TYPE Type = { .Type = FrameType };
      if (Type.LEN) {
          if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->Length) ||
              BufferLength < Frame->Length + *Offset) {
              return FALSE;
          }
      } else {
          QUIC_ANALYSIS_ASSERT(BufferLength >= *Offset);
          Frame->Length = BufferLength - *Offset;
      }
      Frame->Data = Buffer + *Offset;
      *Offset += (uint16_t)Frame->Length;
      return TRUE;
  }
  
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicAckFrequencyFrameEncode(
      _In_ const QUIC_ACK_FREQUENCY_EX * const Frame,
      _Inout_ uint16_t* Offset,
      _In_ uint16_t BufferLength,
      _Out_writes_to_(BufferLength, *Offset) uint8_t* Buffer
      )
  {
      uint16_t RequiredLength =
          QuicVarIntSize(QUIC_FRAME_ACK_FREQUENCY) +
          QuicVarIntSize(Frame->SequenceNumber) +
          QuicVarIntSize(Frame->PacketTolerance) +
          QuicVarIntSize(Frame->UpdateMaxAckDelay) +
          sizeof(uint8_t);    // IgnoreOrder
  
      if (BufferLength < *Offset + RequiredLength) {
          return FALSE;
      }
  
      QUIC_DBG_ASSERT(Frame->IgnoreOrder <= 1); // IgnoreOrder should only be 0 or 1.
  
      Buffer = Buffer + *Offset;
      Buffer = QuicVarIntEncode(QUIC_FRAME_ACK_FREQUENCY, Buffer);
      Buffer = QuicVarIntEncode(Frame->SequenceNumber, Buffer);
      Buffer = QuicVarIntEncode(Frame->PacketTolerance, Buffer);
      Buffer = QuicVarIntEncode(Frame->UpdateMaxAckDelay, Buffer);
      QuicUint8Encode(Frame->IgnoreOrder, Buffer);
      *Offset += RequiredLength;
  
      return TRUE;
  }
  
  _Success_(return != FALSE)
  BOOLEAN
  QuicAckFrequencyFrameDecode(
      _In_ uint16_t BufferLength,
      _In_reads_bytes_(BufferLength)
          const uint8_t * const Buffer,
      _Inout_ uint16_t* Offset,
      _Out_ QUIC_ACK_FREQUENCY_EX* Frame
      )
  {
      if (!QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->SequenceNumber) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->PacketTolerance) ||
          !QuicVarIntDecode(BufferLength, Buffer, Offset, &Frame->UpdateMaxAckDelay) ||
          !QuicUint8tDecode(BufferLength, Buffer, Offset, &Frame->IgnoreOrder) ||
          Frame->IgnoreOrder > 1) { // IgnoreOrder should only be 0 or 1.
          return FALSE;
      }
      return TRUE;
  }
  
  
  _IRQL_requires_max_(DISPATCH_LEVEL)
  BOOLEAN
  QuicFrameLog(
      _In_opt_ QUIC_CONNECTION* Connection,
      _In_ BOOLEAN Rx,
      _In_ uint64_t PacketNumber,
      _In_ uint16_t PacketLength,
      _In_reads_bytes_(PacketLength)
          const uint8_t * const Packet,
      _Inout_ uint16_t* Offset
      )
  {
      QUIC_VAR_INT FrameType INIT_NO_SAL(0);
      if (!QuicVarIntDecode(PacketLength, Packet, Offset, &FrameType)) {
          QuicTraceLogInfo(
              "[conn][%p] ERROR, %s.",
              Connection,
              "Frame type decode failure");
          QuicConnTransportError(Connection, QUIC_ERROR_FRAME_ENCODING_ERROR);
          return FALSE;
      }
  
      if (!QUIC_FRAME_IS_KNOWN(FrameType)) {
          QuicTraceLogVerbose(
              "FrameLogUnknownType: [%c][%cX][%lu] ",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber);
          return FALSE;
      }
  
      *Offset += 1;
  
      switch (FrameType) {
  
      case QUIC_FRAME_PADDING: {
          uint16_t Start = *Offset;
          while (*Offset < PacketLength &&
              Packet[*Offset] == QUIC_FRAME_PADDING) {
              (*Offset) += sizeof(uint8_t);
          }
          QuicTraceLogVerbose(
              "FrameLogPadding: [%c][%cX][%lu]   PADDING Len:%hu",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              (*Offset - Start) + 1);
          break;
      }
  
      case QUIC_FRAME_PING: {
          QuicTraceLogVerbose(
              "FrameLogPing: [%c][%cX][%lu]   PING",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber);
          break;
      }
  
      case QUIC_FRAME_ACK:
      case QUIC_FRAME_ACK_1: {
          QUIC_ACK_EX Frame;
          if (!QuicAckHeaderDecode(PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogAckInvalid: [%c][%cX][%lu]   ACK [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "FrameLogAck: [%c][%cX][%lu]   ACK Largest:%lu Delay:%lu",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              Frame.LargestAcknowledged,
              Frame.AckDelay);
  
          if (Frame.FirstAckBlock == 0) {
              QuicTraceLogVerbose(
                  "FrameLogAckSingleBlock: [%c][%cX][%lu]     %lu",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber,
                  Frame.LargestAcknowledged);
          } else {
              QuicTraceLogVerbose(
                  "FrameLogAckMultiBlock: [%c][%cX][%lu]     %lu - %lu",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber,
                  Frame.LargestAcknowledged - Frame.FirstAckBlock,
                  Frame.LargestAcknowledged);
          }
  
          Frame.LargestAcknowledged -= (Frame.FirstAckBlock + 1);
  
          for (uint64_t i = 0; i < Frame.AdditionalAckBlockCount; i++) {
              QUIC_ACK_BLOCK_EX Block;
              if (!QuicAckBlockDecode(PacketLength, Packet, Offset, &Block)) {
                  QuicTraceLogVerbose(
                      "FrameLogAckInvalidBlock: [%c][%cX][%lu]     [Invalid Block]",
                      PtkConnPre(Connection),
                      PktRxPre(Rx),
                      PacketNumber);
                  return FALSE;
              }
  
              Frame.LargestAcknowledged -= (Block.Gap + 1);
  
              if (Block.AckBlock == 0) {
                  QuicTraceLogVerbose(
                      "FrameLogAckSingleBlock: [%c][%cX][%lu]     %lu",
                      PtkConnPre(Connection),
                      PktRxPre(Rx),
                      PacketNumber,
                      Frame.LargestAcknowledged);
              } else {
                  QuicTraceLogVerbose(
                      "FrameLogAckMultiBlock: [%c][%cX][%lu]     %lu - %lu",
                      PtkConnPre(Connection),
                      PktRxPre(Rx),
                      PacketNumber,
                      Frame.LargestAcknowledged - Block.AckBlock,
                      Frame.LargestAcknowledged);
              }
  
              Frame.LargestAcknowledged -= (Block.AckBlock + 1);
          }
  
          if (FrameType == QUIC_FRAME_ACK_1) {
              QUIC_ACK_ECN_EX Ecn;
              if (!QuicVarIntDecode(PacketLength, Packet, Offset, &Ecn.ECT_0_Count) ||
                  !QuicVarIntDecode(PacketLength, Packet, Offset, &Ecn.ECT_1_Count) ||
                  !QuicVarIntDecode(PacketLength, Packet, Offset, &Ecn.CE_Count)) {
                  QuicTraceLogVerbose(
                      "FrameLogAckEcnInvalid: [%c][%cX][%lu]     ECN [Invalid]",
                      PtkConnPre(Connection),
                      PktRxPre(Rx),
                      PacketNumber);
                  return FALSE;
              }
              QuicTraceLogVerbose(
                  "FrameLogAckEcn: [%c][%cX][%lu]     ECN [ECT0=%lu,ECT1=%lu,CE=%lu]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber,
                  Ecn.ECT_0_Count,
                  Ecn.ECT_1_Count,
                  Ecn.CE_Count);
          }
  
          break;
      }
  
      case QUIC_FRAME_RESET_STREAM: {
          QUIC_RESET_STREAM_EX Frame;
          if (!QuicResetStreamFrameDecode(PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogResetStreamInvalid: [%c][%cX][%lu]   RESET_STREAM [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "FrameLogResetStream: [%c][%cX][%lu]   RESET_STREAM ID:%lu ErrorCode:0x%lX FinalSize:%lu",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              Frame.StreamID,
              Frame.ErrorCode,
              Frame.FinalSize);
          break;
      }
  
      case QUIC_FRAME_STOP_SENDING: {
          QUIC_STOP_SENDING_EX Frame;
          if (!QuicStopSendingFrameDecode(PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogStopSendingInvalid: [%c][%cX][%lu]   STOP_SENDING [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "FrameLogStopSending: [%c][%cX][%lu]   STOP_SENDING ID:%lu Error:0x%lX",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              Frame.StreamID,
              Frame.ErrorCode);
          break;
      }
  
      case QUIC_FRAME_CRYPTO: {
          QUIC_CRYPTO_EX Frame;
          if (!QuicCryptoFrameDecode(PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogCryptoInvalid: [%c][%cX][%lu]   CRYPTO [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "FrameLogCrypto: [%c][%cX][%lu]   CRYPTO Offset:%lu Len:%hu",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              Frame.Offset,
              (uint16_t)Frame.Length);
  
          break;
      }
  
      case QUIC_FRAME_NEW_TOKEN: {
          QUIC_NEW_TOKEN_EX Frame;
          if (!QuicNewTokenFrameDecode(PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogNewTokenInvalid: [%c][%cX][%lu]   NEW_TOKEN [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "FrameLogNewToken: [%c][%cX][%lu]   NEW_TOKEN Length:%lu",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              Frame.TokenLength);
  
          break;
      }
  
      case QUIC_FRAME_STREAM:
      case QUIC_FRAME_STREAM_1:
      case QUIC_FRAME_STREAM_2:
      case QUIC_FRAME_STREAM_3:
      case QUIC_FRAME_STREAM_4:
      case QUIC_FRAME_STREAM_5:
      case QUIC_FRAME_STREAM_6:
      case QUIC_FRAME_STREAM_7: {
          QUIC_STREAM_EX Frame;
          if (!QuicStreamFrameDecode(FrameType, PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogStreamInvalid: [%c][%cX][%lu]   STREAM [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          if (Frame.Fin) {
              QuicTraceLogVerbose(
                  "FrameLogStreamFin: [%c][%cX][%lu]   STREAM ID:%lu Offset:%lu Len:%hu Fin",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber,
                  Frame.StreamID,
                  Frame.Offset,
                  (uint16_t)Frame.Length);
          } else {
              QuicTraceLogVerbose(
                  "FrameLogStream: [%c][%cX][%lu]   STREAM ID:%lu Offset:%lu Len:%hu",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber,
                  Frame.StreamID,
                  Frame.Offset,
                  (uint16_t)Frame.Length);
          }
  
          break;
      }
  
      case QUIC_FRAME_MAX_DATA: {
          QUIC_MAX_DATA_EX Frame;
          if (!QuicMaxDataFrameDecode(PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogMaxDataInvalid: [%c][%cX][%lu]   MAX_DATA [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "FrameLogMaxData: [%c][%cX][%lu]   MAX_DATA Max:%lu",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              Frame.MaximumData);
          break;
      }
  
      case QUIC_FRAME_MAX_STREAM_DATA: {
          QUIC_MAX_STREAM_DATA_EX Frame;
          if (!QuicMaxStreamDataFrameDecode(PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogMaxStreamDataInvalid: [%c][%cX][%lu]   MAX_STREAM_DATA [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "FrameLogMaxStreamData: [%c][%cX][%lu]   MAX_STREAM_DATA ID:%lu Max:%lu",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              Frame.StreamID,
              Frame.MaximumData);
          break;
      }
  
      case QUIC_FRAME_MAX_STREAMS:
      case QUIC_FRAME_MAX_STREAMS_1: {
          QUIC_MAX_STREAMS_EX Frame;
          if (!QuicMaxStreamsFrameDecode(FrameType, PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogMaxStreamsInvalid: [%c][%cX][%lu]   MAX_STREAMS [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "FrameLogMaxStreams: [%c][%cX][%lu]   MAX_STREAMS[%hu] Count:%lu",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              Frame.BidirectionalStreams,
              Frame.MaximumStreams);
          break;
      }
  
      case QUIC_FRAME_DATA_BLOCKED: {
          QUIC_DATA_BLOCKED_EX Frame;
          if (!QuicDataBlockedFrameDecode(PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogDataBlockedInvalid: [%c][%cX][%lu]   DATA_BLOCKED [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
          QuicTraceLogVerbose(
              "FrameLogDataBlocked: [%c][%cX][%lu]   DATA_BLOCKED Limit:%lu",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              Frame.DataLimit);
          break;
      }
  
      case QUIC_FRAME_STREAM_DATA_BLOCKED: {
          QUIC_STREAM_DATA_BLOCKED_EX Frame;
          if (!QuicStreamDataBlockedFrameDecode(PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogStreamDataBlockedInvalid: [%c][%cX][%lu]   STREAM_DATA_BLOCKED [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "FrameLogStreamDataBlocked: [%c][%cX][%lu]   STREAM_DATA_BLOCKED ID:%lu Limit:%lu",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              Frame.StreamID,
              Frame.StreamDataLimit);
          break;
      }
  
      case QUIC_FRAME_STREAMS_BLOCKED:
      case QUIC_FRAME_STREAMS_BLOCKED_1: {
          QUIC_STREAMS_BLOCKED_EX Frame;
          if (!QuicStreamsBlockedFrameDecode(FrameType, PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogStreamsBlockedInvalid: [%c][%cX][%lu]   STREAMS_BLOCKED [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "FrameLogStreamsBlocked: [%c][%cX][%lu]   STREAMS_BLOCKED[%hu] ID:%lu",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              Frame.BidirectionalStreams,
              Frame.StreamLimit);
          break;
      }
  
      case QUIC_FRAME_NEW_CONNECTION_ID: {
          QUIC_NEW_CONNECTION_ID_EX Frame;
          if (!QuicNewConnectionIDFrameDecode(PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogNewConnectionIDInvalid: [%c][%cX][%lu]   NEW_CONN_ID [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "FrameLogNewConnectionID: [%c][%cX][%lu]   NEW_CONN_ID Seq:%lu RPT:%lu CID:%s Token:%s",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              Frame.Sequence,
              Frame.RetirePriorTo,
              QuicCidBufToStr(Frame.Buffer, Frame.Length).Buffer,
              QuicCidBufToStr(Frame.Buffer + Frame.Length, QUIC_STATELESS_RESET_TOKEN_LENGTH).Buffer);
          break;
      }
  
      case QUIC_FRAME_RETIRE_CONNECTION_ID: {
          QUIC_RETIRE_CONNECTION_ID_EX Frame;
          if (!QuicRetireConnectionIDFrameDecode(PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogRetireConnectionIDInvalid: [%c][%cX][%lu]   RETIRE_CONN_ID [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "FrameLogRetireConnectionID: [%c][%cX][%lu]   RETIRE_CONN_ID Seq:%lu",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              Frame.Sequence);
          break;
      }
  
      case QUIC_FRAME_PATH_CHALLENGE: {
          QUIC_PATH_CHALLENGE_EX Frame;
          if (!QuicPathChallengeFrameDecode(PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogPathChallengeInvalid: [%c][%cX][%lu]   PATH_CHALLENGE [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "FrameLogPathChallenge: [%c][%cX][%lu]   PATH_CHALLENGE [%lu]",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              QuicByteSwapUint64(*(uint64_t*)Frame.Data));
          break;
      }
  
      case QUIC_FRAME_PATH_RESPONSE: {
          QUIC_PATH_RESPONSE_EX Frame;
          if (!QuicPathChallengeFrameDecode(PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogPathResponseInvalid: [%c][%cX][%lu]   PATH_RESPONSE [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "FrameLogPathResponse: [%c][%cX][%lu]   PATH_RESPONSE [%lu]",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              QuicByteSwapUint64(*(uint64_t*)Frame.Data));
          break;
      }
  
      case QUIC_FRAME_CONNECTION_CLOSE:
      case QUIC_FRAME_CONNECTION_CLOSE_1: {
          QUIC_CONNECTION_CLOSE_EX Frame;
          if (!QuicConnCloseFrameDecode(FrameType, PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogConnectionCloseInvalid: [%c][%cX][%lu]   CONN_CLOSE [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          if (Frame.ApplicationClosed) {
              QuicTraceLogVerbose(
                  "FrameLogConnectionCloseApp: [%c][%cX][%lu]   CONN_CLOSE (App) ErrorCode:0x%lX",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber,
                  Frame.ErrorCode);
          } else {
              QuicTraceLogVerbose(
                  "FrameLogConnectionClose: [%c][%cX][%lu]   CONN_CLOSE ErrorCode:0x%lX FrameType:%lu",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber,
                  Frame.ErrorCode,
                  Frame.FrameType);
          }
          break;
      }
  
      case QUIC_FRAME_HANDSHAKE_DONE: {
          QuicTraceLogVerbose(
              "FrameLogHandshakeDone: [%c][%cX][%lu]   HANDSHAKE_DONE",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber);
          break;
      }
  
      case QUIC_FRAME_DATAGRAM:
      case QUIC_FRAME_DATAGRAM_1: {
          QUIC_DATAGRAM_EX Frame;
          if (!QuicDatagramFrameDecode(FrameType, PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "FrameLogDatagramInvalid: [%c][%cX][%lu]   DATAGRAM [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
          QuicTraceLogVerbose(
              "FrameLogDatagram: [%c][%cX][%lu]   DATAGRAM Len:%hu",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              (uint16_t)Frame.Length);
          break;
      }
  
      case QUIC_FRAME_ACK_FREQUENCY: {
          QUIC_ACK_FREQUENCY_EX Frame;
          if (!QuicAckFrequencyFrameDecode(PacketLength, Packet, Offset, &Frame)) {
              QuicTraceLogVerbose(
                  "[%c][%cX][%lu]   ACK_FREQUENCY [Invalid]",
                  PtkConnPre(Connection),
                  PktRxPre(Rx),
                  PacketNumber);
              return FALSE;
          }
  
          QuicTraceLogVerbose(
              "[%c][%cX][%lu]   ACK_FREQUENCY SeqNum:%lu PktTolerance:%lu MaxAckDelay:%lu IgnoreOrder:%hhu",
              PtkConnPre(Connection),
              PktRxPre(Rx),
              PacketNumber,
              Frame.SequenceNumber,
              Frame.PacketTolerance,
              Frame.UpdateMaxAckDelay,
              Frame.IgnoreOrder);
          break;
      }
  
  
  
  
      default:
          QUIC_FRE_ASSERT(FALSE);
          break;
      }
  
      return TRUE;
  }
  
  _IRQL_requires_max_(DISPATCH_LEVEL)
  void
  QuicFrameLogAll(
      _In_opt_ QUIC_CONNECTION* Connection,
      _In_ BOOLEAN Rx,
      _In_ uint64_t PacketNumber,
      _In_ uint16_t PacketLength,
      _In_reads_bytes_(PacketLength)
          const uint8_t * const Packet,
      _In_ uint16_t Offset
      )
  {
      BOOLEAN ProcessFrames = TRUE;
      while (ProcessFrames && Offset < PacketLength) {
          ProcessFrames =
              QuicFrameLog(
                  Connection,
                  Rx,
                  PacketNumber,
                  PacketLength,
                  Packet,
                  &Offset);
      }
  }
  