// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

// Author: kenton@google.com (Kenton Varda)
//  Based on original Protocol Buffers design by
//  Sanjay Ghemawat, Jeff Dean, and others.
//
// This file contains tests and benchmarks.

#include "google/protobuf/io/coded_stream.h"

#include <limits.h>

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <ios>
#include <memory>
#include <ostream>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/base/casts.h"
#include "absl/base/log_severity.h"
#include "absl/base/macros.h"
#include "absl/log/absl_log.h"
#include "absl/log/scoped_mock_log.h"
#include "absl/strings/cord.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"

#include "google/protobuf/testing/googletest.h"


// Must be included last.
#include "google/protobuf/port_def.inc"

namespace google {
namespace protobuf {
namespace io {
namespace {

class CodedStreamTest : public testing::Test {
 protected:
  // Buffer used during most of the tests. This assumes tests run sequentially.
  static constexpr int kBufferSize = 1024 * 64;
  static uint8_t buffer_[kBufferSize];
};

uint8_t CodedStreamTest::buffer_[CodedStreamTest::kBufferSize] = {};

// We test each operation over a variety of block sizes to insure that
// we test cases where reads or writes cross buffer boundaries, cases
// where they don't, and cases where there is so much buffer left that
// we can use special optimized paths that don't worry about bounds
// checks.
const int kBlockSizes[] = {1, 2, 3, 5, 7, 13, 32, 1024};

// In several ReadCord test functions, we either clear the Cord before ReadCord
// calls or not.
const bool kResetCords[] = {false, true};

// -------------------------------------------------------------------
// Varint tests.

struct VarintCase {
  uint8_t bytes[10];  // Encoded bytes.
  size_t size;        // Encoded size, in bytes.
  uint64_t value;     // Parsed value.
};

inline std::ostream& operator<<(std::ostream& os, const VarintCase& c) {
  return os << c.value;
}

VarintCase kVarintCases[] = {
    // 32-bit values
    {{0x00}, 1, 0},
    {{0x01}, 1, 1},
    {{0x7f}, 1, 127},
    {{0xa2, 0x74}, 2, (0x22 << 0) | (0x74 << 7)},  // 14882
    {{0xbe, 0xf7, 0x92, 0x84, 0x0b},
     5,  // 2961488830
     (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
         (uint64_t{0x0bu} << 28)},

    // 64-bit
    {{0xbe, 0xf7, 0x92, 0x84, 0x1b},
     5,  // 7256456126
     (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
         (uint64_t{0x1bu} << 28)},
    {{0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49},
     8,  // 41256202580718336
     (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
         (uint64_t{0x43u} << 28) | (uint64_t{0x49u} << 35) |
         (uint64_t{0x24u} << 42) | (uint64_t{0x49u} << 49)},
    // 11964378330978735131
    {{0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01},
     10,
     (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
         (uint64_t{0x3bu} << 28) | (uint64_t{0x56u} << 35) |
         (uint64_t{0x00u} << 42) | (uint64_t{0x05u} << 49) |
         (uint64_t{0x26u} << 56) | (uint64_t{0x01u} << 63)},
};

class VarintCasesWithSizes
    : public CodedStreamTest,
      public testing::WithParamInterface<std::tuple<VarintCase, int>> {};

TEST_P(VarintCasesWithSizes, ReadVarint32) {
  const VarintCase& kVarintCases_case = std::get<0>(GetParam());
  const int& kBlockSizes_case = std::get<1>(GetParam());
  memcpy(buffer_, kVarintCases_case.bytes, kVarintCases_case.size);
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    uint32_t value;
    EXPECT_TRUE(coded_input.ReadVarint32(&value));
    EXPECT_EQ(static_cast<uint32_t>(kVarintCases_case.value), value);
  }

  EXPECT_EQ(kVarintCases_case.size, input.ByteCount());
}

TEST_P(VarintCasesWithSizes, ReadTag) {
  const VarintCase& kVarintCases_case = std::get<0>(GetParam());
  const int& kBlockSizes_case = std::get<1>(GetParam());
  memcpy(buffer_, kVarintCases_case.bytes, kVarintCases_case.size);
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    uint32_t expected_value = static_cast<uint32_t>(kVarintCases_case.value);
    EXPECT_EQ(expected_value, coded_input.ReadTag());

    EXPECT_TRUE(coded_input.LastTagWas(expected_value));
    EXPECT_FALSE(coded_input.LastTagWas(expected_value + 1));
  }

  EXPECT_EQ(kVarintCases_case.size, input.ByteCount());
}

// This is the regression test that verifies that there is no issues
// with the empty input buffers handling.
TEST_F(CodedStreamTest, EmptyInputBeforeEos) {
  class In : public ZeroCopyInputStream {
   public:
    In() : count_(0) {}

   private:
    bool Next(const void** data, int* size) override {
      *data = nullptr;
      *size = 0;
      return count_++ < 2;
    }
    void BackUp(int count) override {
      ABSL_LOG(FATAL) << "Tests never call this.";
    }
    bool Skip(int count) override {
      ABSL_LOG(FATAL) << "Tests never call this.";
      return false;
    }
    int64_t ByteCount() const override { return 0; }
    int count_;
  } in;
  CodedInputStream input(&in);
  input.ReadTagNoLastTag();
  EXPECT_TRUE(input.ConsumedEntireMessage());
}

class VarintCases : public CodedStreamTest,
                    public testing::WithParamInterface<VarintCase> {};

TEST_P(VarintCases, ExpectTag) {
  const VarintCase& kVarintCases_case = GetParam();
  // Leave one byte at the beginning of the buffer so we can read it
  // to force the first buffer to be loaded.
  buffer_[0] = '\0';
  memcpy(buffer_ + 1, kVarintCases_case.bytes, kVarintCases_case.size);
  ArrayInputStream input(buffer_, sizeof(buffer_));

  {
    CodedInputStream coded_input(&input);

    // Read one byte to force coded_input.Refill() to be called.  Otherwise,
    // ExpectTag() will return a false negative.
    uint8_t dummy;
    coded_input.ReadRaw(&dummy, 1);
    EXPECT_EQ((uint)'\0', (uint)dummy);

    uint32_t expected_value = static_cast<uint32_t>(kVarintCases_case.value);

    // ExpectTag() produces false negatives for large values.
    if (kVarintCases_case.size <= 2) {
      EXPECT_FALSE(coded_input.ExpectTag(expected_value + 1));
      EXPECT_TRUE(coded_input.ExpectTag(expected_value));
    } else {
      EXPECT_FALSE(coded_input.ExpectTag(expected_value));
    }
  }

  if (kVarintCases_case.size <= 2) {
    EXPECT_EQ(kVarintCases_case.size + 1, input.ByteCount());
  } else {
    EXPECT_EQ(1, input.ByteCount());
  }
}

TEST_P(VarintCases, ExpectTagFromArray) {
  const VarintCase& kVarintCases_case = GetParam();
  memcpy(buffer_, kVarintCases_case.bytes, kVarintCases_case.size);

  const uint32_t expected_value =
      static_cast<uint32_t>(kVarintCases_case.value);

  // If the expectation succeeds, it should return a pointer past the tag.
  if (kVarintCases_case.size <= 2) {
    EXPECT_TRUE(nullptr == CodedInputStream::ExpectTagFromArray(
                            buffer_, expected_value + 1));
    EXPECT_TRUE(buffer_ + kVarintCases_case.size ==
                CodedInputStream::ExpectTagFromArray(buffer_, expected_value));
  } else {
    EXPECT_TRUE(nullptr ==
                CodedInputStream::ExpectTagFromArray(buffer_, expected_value));
  }
}

TEST_P(VarintCasesWithSizes, ReadVarint64) {
  const VarintCase& kVarintCases_case = std::get<0>(GetParam());
  const int& kBlockSizes_case = std::get<1>(GetParam());
  memcpy(buffer_, kVarintCases_case.bytes, kVarintCases_case.size);
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    uint64_t value;
    EXPECT_TRUE(coded_input.ReadVarint64(&value));
    EXPECT_EQ(kVarintCases_case.value, value);
  }

  EXPECT_EQ(kVarintCases_case.size, input.ByteCount());
}

TEST_P(VarintCasesWithSizes, WriteVarint32) {
  const VarintCase& kVarintCases_case = std::get<0>(GetParam());
  const int& kBlockSizes_case = std::get<1>(GetParam());
  if (kVarintCases_case.value > uint64_t{0x00000000FFFFFFFFu}) {
    // Skip this test for the 64-bit values.
    return;
  }

  ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedOutputStream coded_output(&output);

    coded_output.WriteVarint32(static_cast<uint32_t>(kVarintCases_case.value));
    EXPECT_FALSE(coded_output.HadError());

    EXPECT_EQ(kVarintCases_case.size, coded_output.ByteCount());
  }

  EXPECT_EQ(kVarintCases_case.size, output.ByteCount());
  EXPECT_EQ(0,
            memcmp(buffer_, kVarintCases_case.bytes, kVarintCases_case.size));
}

TEST_P(VarintCasesWithSizes, WriteVarint64) {
  const VarintCase& kVarintCases_case = std::get<0>(GetParam());
  const int& kBlockSizes_case = std::get<1>(GetParam());
  ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedOutputStream coded_output(&output);

    coded_output.WriteVarint64(kVarintCases_case.value);
    EXPECT_FALSE(coded_output.HadError());

    EXPECT_EQ(kVarintCases_case.size, coded_output.ByteCount());
  }

  EXPECT_EQ(kVarintCases_case.size, output.ByteCount());
  EXPECT_EQ(0,
            memcmp(buffer_, kVarintCases_case.bytes, kVarintCases_case.size));
}

class SignedVarintCasesWithSizes
    : public CodedStreamTest,
      public testing::WithParamInterface<std::tuple<int32_t, int>> {};

int32_t kSignExtendedVarintCases[] = {0, 1, -1, 1237894, -37895138};

TEST_P(SignedVarintCasesWithSizes, WriteVarint32SignExtended) {
  const int32_t& kSignExtendedVarintCases_case = std::get<0>(GetParam());
  const int& kBlockSizes_case = std::get<1>(GetParam());
  ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedOutputStream coded_output(&output);

    coded_output.WriteVarint32SignExtended(kSignExtendedVarintCases_case);
    EXPECT_FALSE(coded_output.HadError());

    if (kSignExtendedVarintCases_case < 0) {
      EXPECT_EQ(10, coded_output.ByteCount());
    } else {
      EXPECT_LE(coded_output.ByteCount(), 5);
    }
  }

  if (kSignExtendedVarintCases_case < 0) {
    EXPECT_EQ(10, output.ByteCount());
  } else {
    EXPECT_LE(output.ByteCount(), 5);
  }

  // Read value back in as a varint64 and insure it matches.
  ArrayInputStream input(buffer_, sizeof(buffer_));

  {
    CodedInputStream coded_input(&input);

    uint64_t value;
    EXPECT_TRUE(coded_input.ReadVarint64(&value));

    EXPECT_EQ(kSignExtendedVarintCases_case, static_cast<int64_t>(value));
  }

  EXPECT_EQ(output.ByteCount(), input.ByteCount());
}


// -------------------------------------------------------------------
// Varint failure test.

struct VarintErrorCase {
  uint8_t bytes[12];
  size_t size;
  bool can_parse;
  const char* name;
};

inline std::ostream& operator<<(std::ostream& os, const VarintErrorCase& c) {
  return os << "size " << c.size;
}

const VarintErrorCase kVarintErrorCases[] = {
    // Control case.  (Insures that there isn't something else wrong that
    // makes parsing always fail.)
    {{0x00}, 1, true, "Control"},

    // No input data.
    {{}, 0, false, "No_Input"},

    // Input ends unexpectedly.
    {{0xf0, 0xab}, 2, false, "Input_Ends_Unexpectedly"},

    // Input ends unexpectedly after 32 bits.
    {{0xf0, 0xab, 0xc9, 0x9a, 0xf8, 0xb2},
     6,
     false,
     "Input_Ends_Unexpectedly_After_32_Bits"},

    // Longer than 10 bytes.
    {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01},
     11,
     false,
     "Longer_Than_10_Bytes"},
};

class VarintErrorCasesWithSizes
    : public CodedStreamTest,
      public testing::WithParamInterface<std::tuple<VarintErrorCase, int>> {};

TEST_P(VarintErrorCasesWithSizes, ReadVarint32Error) {
  VarintErrorCase kVarintErrorCases_case = std::get<0>(GetParam());
  int kBlockSizes_case = std::get<1>(GetParam());
  memcpy(buffer_, kVarintErrorCases_case.bytes, kVarintErrorCases_case.size);
  ArrayInputStream input(buffer_, static_cast<int>(kVarintErrorCases_case.size),
                         kBlockSizes_case);
  CodedInputStream coded_input(&input);

  uint32_t value;
  EXPECT_EQ(kVarintErrorCases_case.can_parse, coded_input.ReadVarint32(&value));
}

TEST_P(VarintErrorCasesWithSizes,
       ReadVarint32Error_LeavesValueInInitializedState) {
  VarintErrorCase kVarintErrorCases_case = std::get<0>(GetParam());
  int kBlockSizes_case = std::get<1>(GetParam());
  memcpy(buffer_, kVarintErrorCases_case.bytes, kVarintErrorCases_case.size);
  ArrayInputStream input(buffer_, static_cast<int>(kVarintErrorCases_case.size),
                         kBlockSizes_case);
  CodedInputStream coded_input(&input);

  uint32_t value = 0;
  EXPECT_EQ(kVarintErrorCases_case.can_parse, coded_input.ReadVarint32(&value));
  // While the specific value following a failure is not critical, we do want to
  // ensure that it doesn't get set to an uninitialized value. (This check fails
  // in MSAN mode if value has been set to an uninitialized value.)
  EXPECT_EQ(value, value);
}

TEST_P(VarintErrorCasesWithSizes, ReadVarint64Error) {
  VarintErrorCase kVarintErrorCases_case = std::get<0>(GetParam());
  int kBlockSizes_case = std::get<1>(GetParam());
  memcpy(buffer_, kVarintErrorCases_case.bytes, kVarintErrorCases_case.size);
  ArrayInputStream input(buffer_, static_cast<int>(kVarintErrorCases_case.size),
                         kBlockSizes_case);
  CodedInputStream coded_input(&input);

  uint64_t value;
  EXPECT_EQ(kVarintErrorCases_case.can_parse, coded_input.ReadVarint64(&value));
}

TEST_P(VarintErrorCasesWithSizes,
       ReadVarint64Error_LeavesValueInInitializedState) {
  VarintErrorCase kVarintErrorCases_case = std::get<0>(GetParam());
  int kBlockSizes_case = std::get<1>(GetParam());
  memcpy(buffer_, kVarintErrorCases_case.bytes, kVarintErrorCases_case.size);
  ArrayInputStream input(buffer_, static_cast<int>(kVarintErrorCases_case.size),
                         kBlockSizes_case);
  CodedInputStream coded_input(&input);

  uint64_t value = 0;
  EXPECT_EQ(kVarintErrorCases_case.can_parse, coded_input.ReadVarint64(&value));
  // While the specific value following a failure is not critical, we do want to
  // ensure that it doesn't get set to an uninitialized value. (This check fails
  // in MSAN mode if value has been set to an uninitialized value.)
  EXPECT_EQ(value, value);
}

// -------------------------------------------------------------------
// VarintSize

struct VarintSizeCase {
  uint64_t value;
  int size;
};

inline std::ostream& operator<<(std::ostream& os, const VarintSizeCase& c) {
  return os << c.value;
}

VarintSizeCase kVarintSizeCases[] = {
    {0u, 1},
    {1u, 1},
    {127u, 1},
    {128u, 2},
    {758923u, 3},
    {4000000000u, 5},
    {uint64_t{41256202580718336u}, 8},
    {uint64_t{11964378330978735131u}, 10},
};

class VarintSizeCases : public CodedStreamTest,
                        public testing::WithParamInterface<VarintSizeCase> {};

TEST_P(VarintSizeCases, VarintSize32) {
  VarintSizeCase kVarintSizeCases_case = GetParam();
  if (kVarintSizeCases_case.value > 0xffffffffu) {
    // Skip 64-bit values.
    return;
  }

  EXPECT_EQ(kVarintSizeCases_case.size,
            CodedOutputStream::VarintSize32(
                static_cast<uint32_t>(kVarintSizeCases_case.value)));
}

TEST_P(VarintSizeCases, VarintSize64) {
  VarintSizeCase kVarintSizeCases_case = GetParam();
  EXPECT_EQ(kVarintSizeCases_case.size,
            CodedOutputStream::VarintSize64(kVarintSizeCases_case.value));
}

TEST_F(CodedStreamTest, VarintSize32PowersOfTwo) {
  int expected = 1;
  for (int i = 1; i < 32; i++) {
    if (i % 7 == 0) {
      expected += 1;
    }
    EXPECT_EQ(expected, CodedOutputStream::VarintSize32(
                            static_cast<uint32_t>(0x1u << i)));
  }
}

TEST_F(CodedStreamTest, VarintSize64PowersOfTwo) {
  int expected = 1;
  for (int i = 1; i < 64; i++) {
    if (i % 7 == 0) {
      expected += 1;
    }
    EXPECT_EQ(expected, CodedOutputStream::VarintSize64(uint64_t{1} << i));
  }
}

// -------------------------------------------------------------------
// Fixed-size int tests

struct Fixed16Case {
  uint8_t bytes[sizeof(uint16_t)];  // Encoded bytes.
  uint32_t value;                   // Parsed value.
};

struct Fixed32Case {
  uint8_t bytes[sizeof(uint32_t)];  // Encoded bytes.
  uint32_t value;                   // Parsed value.
};

struct Fixed64Case {
  uint8_t bytes[sizeof(uint64_t)];  // Encoded bytes.
  uint64_t value;                   // Parsed value.
};

class Fixed16Cases : public CodedStreamTest,
                     public testing::WithParamInterface<Fixed16Case> {};

class Fixed16CasesWithSizes
    : public CodedStreamTest,
      public testing::WithParamInterface<std::tuple<Fixed16Case, int>> {};

class Fixed32Cases : public CodedStreamTest,
                     public testing::WithParamInterface<Fixed32Case> {};

class Fixed32CasesWithSizes
    : public CodedStreamTest,
      public testing::WithParamInterface<std::tuple<Fixed32Case, int>> {};

class Fixed64Cases : public CodedStreamTest,
                     public testing::WithParamInterface<Fixed64Case> {};

class Fixed64CasesWithSizes
    : public CodedStreamTest,
      public testing::WithParamInterface<std::tuple<Fixed64Case, int>> {};

inline std::ostream& operator<<(std::ostream& os, const Fixed32Case& c) {
  return os << "0x" << std::hex << c.value << std::dec;
}

inline std::ostream& operator<<(std::ostream& os, const Fixed64Case& c) {
  return os << "0x" << std::hex << c.value << std::dec;
}

Fixed16Case kFixed16Cases[] = {
    {{0xef, 0xcd}, 0xcdefu},
    {{0x12, 0x34}, 0x3412u},
};

Fixed32Case kFixed32Cases[] = {
    {{0xef, 0xcd, 0xab, 0x90}, 0x90abcdefu},
    {{0x12, 0x34, 0x56, 0x78}, 0x78563412u},
};

Fixed64Case kFixed64Cases[] = {
    {{0xef, 0xcd, 0xab, 0x90, 0x12, 0x34, 0x56, 0x78},
     uint64_t{0x7856341290abcdefu}},
    {{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88},
     uint64_t{0x8877665544332211u}},
};

TEST_P(Fixed16CasesWithSizes, ReadLittleEndian16) {
  Fixed16Case kFixed16Cases_case = std::get<0>(GetParam());
  int kBlockSizes_case = std::get<1>(GetParam());
  memcpy(buffer_, kFixed16Cases_case.bytes, sizeof(kFixed16Cases_case.bytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    uint16_t value;
    EXPECT_TRUE(coded_input.ReadLittleEndian16(&value));
    EXPECT_EQ(kFixed16Cases_case.value, value);
  }

  EXPECT_EQ(sizeof(uint16_t), input.ByteCount());
}

TEST_P(Fixed32CasesWithSizes, ReadLittleEndian32) {
  Fixed32Case kFixed32Cases_case = std::get<0>(GetParam());
  int kBlockSizes_case = std::get<1>(GetParam());
  memcpy(buffer_, kFixed32Cases_case.bytes, sizeof(kFixed32Cases_case.bytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    uint32_t value;
    EXPECT_TRUE(coded_input.ReadLittleEndian32(&value));
    EXPECT_EQ(kFixed32Cases_case.value, value);
  }

  EXPECT_EQ(sizeof(uint32_t), input.ByteCount());
}

TEST_P(Fixed64CasesWithSizes, ReadLittleEndian64) {
  Fixed64Case kFixed64Cases_case = std::get<0>(GetParam());
  int kBlockSizes_case = std::get<1>(GetParam());
  memcpy(buffer_, kFixed64Cases_case.bytes, sizeof(kFixed64Cases_case.bytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    uint64_t value;
    EXPECT_TRUE(coded_input.ReadLittleEndian64(&value));
    EXPECT_EQ(kFixed64Cases_case.value, value);
  }

  EXPECT_EQ(sizeof(uint64_t), input.ByteCount());
}

TEST_P(Fixed16CasesWithSizes, WriteLittleEndian16) {
  Fixed16Case kFixed16Cases_case = std::get<0>(GetParam());
  int kBlockSizes_case = std::get<1>(GetParam());
  ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedOutputStream coded_output(&output);

    coded_output.WriteLittleEndian16(kFixed16Cases_case.value);
    EXPECT_FALSE(coded_output.HadError());

    EXPECT_EQ(sizeof(uint16_t), coded_output.ByteCount());
  }

  EXPECT_EQ(sizeof(uint16_t), output.ByteCount());
  EXPECT_EQ(0, memcmp(buffer_, kFixed16Cases_case.bytes, sizeof(uint16_t)));
}

TEST_P(Fixed32CasesWithSizes, WriteLittleEndian32) {
  Fixed32Case kFixed32Cases_case = std::get<0>(GetParam());
  int kBlockSizes_case = std::get<1>(GetParam());
  ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedOutputStream coded_output(&output);

    coded_output.WriteLittleEndian32(kFixed32Cases_case.value);
    EXPECT_FALSE(coded_output.HadError());

    EXPECT_EQ(sizeof(uint32_t), coded_output.ByteCount());
  }

  EXPECT_EQ(sizeof(uint32_t), output.ByteCount());
  EXPECT_EQ(0, memcmp(buffer_, kFixed32Cases_case.bytes, sizeof(uint32_t)));
}

TEST_P(Fixed64CasesWithSizes, WriteLittleEndian64) {
  Fixed64Case kFixed64Cases_case = std::get<0>(GetParam());
  int kBlockSizes_case = std::get<1>(GetParam());
  ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedOutputStream coded_output(&output);

    coded_output.WriteLittleEndian64(kFixed64Cases_case.value);
    EXPECT_FALSE(coded_output.HadError());

    EXPECT_EQ(sizeof(uint64_t), coded_output.ByteCount());
  }

  EXPECT_EQ(sizeof(uint64_t), output.ByteCount());
  EXPECT_EQ(0, memcmp(buffer_, kFixed64Cases_case.bytes, sizeof(uint64_t)));
}

// Tests using the static methods to read fixed-size values from raw arrays.

TEST_P(Fixed16Cases, ReadLittleEndian16FromArray) {
  Fixed16Case kFixed16Cases_case = GetParam();
  memcpy(buffer_, kFixed16Cases_case.bytes, sizeof(kFixed16Cases_case.bytes));

  uint16_t value;
  const uint8_t* end =
      CodedInputStream::ReadLittleEndian16FromArray(buffer_, &value);
  EXPECT_EQ(kFixed16Cases_case.value, value);
  EXPECT_TRUE(end == buffer_ + sizeof(value));
}

TEST_P(Fixed32Cases, ReadLittleEndian32FromArray) {
  Fixed32Case kFixed32Cases_case = GetParam();
  memcpy(buffer_, kFixed32Cases_case.bytes, sizeof(kFixed32Cases_case.bytes));

  uint32_t value;
  const uint8_t* end =
      CodedInputStream::ReadLittleEndian32FromArray(buffer_, &value);
  EXPECT_EQ(kFixed32Cases_case.value, value);
  EXPECT_TRUE(end == buffer_ + sizeof(value));
}

TEST_P(Fixed64Cases, ReadLittleEndian64FromArray) {
  Fixed64Case kFixed64Cases_case = GetParam();
  memcpy(buffer_, kFixed64Cases_case.bytes, sizeof(kFixed64Cases_case.bytes));

  uint64_t value;
  const uint8_t* end =
      CodedInputStream::ReadLittleEndian64FromArray(buffer_, &value);
  EXPECT_EQ(kFixed64Cases_case.value, value);
  EXPECT_TRUE(end == buffer_ + sizeof(value));
}

// -------------------------------------------------------------------
// Raw reads and writes

const char kRawBytes[] = "Some bytes which will be written and read raw.";

class BlockSizes : public CodedStreamTest,
                   public testing::WithParamInterface<int> {};

TEST_P(BlockSizes, ReadRaw) {
  int kBlockSizes_case = GetParam();
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);
  char read_buffer[sizeof(kRawBytes)];

  {
    CodedInputStream coded_input(&input);

    EXPECT_TRUE(coded_input.ReadRaw(read_buffer, sizeof(kRawBytes)));
    EXPECT_EQ(0, memcmp(kRawBytes, read_buffer, sizeof(kRawBytes)));
  }

  EXPECT_EQ(sizeof(kRawBytes), input.ByteCount());
}

TEST_P(BlockSizes, WriteRaw) {
  int kBlockSizes_case = GetParam();
  ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedOutputStream coded_output(&output);

    coded_output.WriteRaw(kRawBytes, sizeof(kRawBytes));
    EXPECT_FALSE(coded_output.HadError());

    EXPECT_EQ(sizeof(kRawBytes), coded_output.ByteCount());
  }

  EXPECT_EQ(sizeof(kRawBytes), output.ByteCount());
  EXPECT_EQ(0, memcmp(buffer_, kRawBytes, sizeof(kRawBytes)));
}

TEST_P(BlockSizes, ReadString) {
  int kBlockSizes_case = GetParam();
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    std::string str;
    EXPECT_TRUE(coded_input.ReadString(&str, strlen(kRawBytes)));
    EXPECT_EQ(kRawBytes, str);
  }

  EXPECT_EQ(strlen(kRawBytes), input.ByteCount());
}

// Check to make sure ReadString doesn't crash on impossibly large strings.
TEST_P(BlockSizes, ReadStringImpossiblyLarge) {
  int kBlockSizes_case = GetParam();
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    std::string str;
    // Try to read a gigabyte.
    EXPECT_FALSE(coded_input.ReadString(&str, 1 << 30));
  }
}

TEST_F(CodedStreamTest, ReadStringImpossiblyLargeFromStringOnStack) {
  // Same test as above, except directly use a buffer. This used to cause
  // crashes while the above did not.
  uint8_t buffer[8] = {};
  CodedInputStream coded_input(buffer, 8);
  std::string str;
  EXPECT_FALSE(coded_input.ReadString(&str, 1 << 30));
}

TEST_F(CodedStreamTest, ReadStringImpossiblyLargeFromStringOnHeap) {
  std::unique_ptr<uint8_t[]> buffer(new uint8_t[8]);
  CodedInputStream coded_input(buffer.get(), 8);
  std::string str;
  EXPECT_FALSE(coded_input.ReadString(&str, 1 << 30));
}

TEST_P(BlockSizes, ReadStringReservesMemoryOnTotalLimit) {
  int kBlockSizes_case = GetParam();
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);
    coded_input.SetTotalBytesLimit(sizeof(kRawBytes));
    EXPECT_EQ(sizeof(kRawBytes), coded_input.BytesUntilTotalBytesLimit());

    std::string str;
    EXPECT_TRUE(coded_input.ReadString(&str, strlen(kRawBytes)));
    EXPECT_EQ(sizeof(kRawBytes) - strlen(kRawBytes),
              coded_input.BytesUntilTotalBytesLimit());
    EXPECT_EQ(kRawBytes, str);
    // TODO: Replace with a more meaningful test (see cl/60966023).
    EXPECT_GE(str.capacity(), strlen(kRawBytes));
  }

  EXPECT_EQ(strlen(kRawBytes), input.ByteCount());
}

TEST_P(BlockSizes, ReadStringReservesMemoryOnPushedLimit) {
  int kBlockSizes_case = GetParam();
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);
    coded_input.PushLimit(sizeof(buffer_));

    std::string str;
    EXPECT_TRUE(coded_input.ReadString(&str, strlen(kRawBytes)));
    EXPECT_EQ(kRawBytes, str);
    // TODO: Replace with a more meaningful test (see cl/60966023).
    EXPECT_GE(str.capacity(), strlen(kRawBytes));
  }

  EXPECT_EQ(strlen(kRawBytes), input.ByteCount());
}

TEST_F(CodedStreamTest, ReadStringNoReservationIfLimitsNotSet) {
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  // Buffer size in the input must be smaller than sizeof(kRawBytes),
  // otherwise check against capacity will fail as ReadStringInline()
  // will handle the reading and will reserve the memory as needed.
  ArrayInputStream input(buffer_, sizeof(buffer_), 32);

  {
    CodedInputStream coded_input(&input);

    std::string str;
    EXPECT_TRUE(coded_input.ReadString(&str, strlen(kRawBytes)));
    EXPECT_EQ(kRawBytes, str);
    // Note: this check depends on string class implementation. It
    // expects that string will allocate more than strlen(kRawBytes)
    // if the content of kRawBytes is appended to string in small
    // chunks.
    // TODO: Replace with a more meaningful test (see cl/60966023).
    EXPECT_GE(str.capacity(), strlen(kRawBytes));
  }

  EXPECT_EQ(strlen(kRawBytes), input.ByteCount());
}

TEST_F(CodedStreamTest, ReadStringNoReservationSizeIsNegative) {
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  // Buffer size in the input must be smaller than sizeof(kRawBytes),
  // otherwise check against capacity will fail as ReadStringInline()
  // will handle the reading and will reserve the memory as needed.
  ArrayInputStream input(buffer_, sizeof(buffer_), 32);

  {
    CodedInputStream coded_input(&input);
    coded_input.PushLimit(sizeof(buffer_));

    std::string str;
    EXPECT_FALSE(coded_input.ReadString(&str, -1));
    // Note: this check depends on string class implementation. It
    // expects that string will always allocate the same amount of
    // memory for an empty string.
    EXPECT_EQ(std::string().capacity(), str.capacity());
  }
}

TEST_F(CodedStreamTest, ReadStringNoReservationSizeIsLarge) {
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  // Buffer size in the input must be smaller than sizeof(kRawBytes),
  // otherwise check against capacity will fail as ReadStringInline()
  // will handle the reading and will reserve the memory as needed.
  ArrayInputStream input(buffer_, sizeof(buffer_), 32);

  {
    CodedInputStream coded_input(&input);
    coded_input.PushLimit(sizeof(buffer_));

    std::string str;
    EXPECT_FALSE(coded_input.ReadString(&str, 1 << 30));
    EXPECT_GT(1 << 30, str.capacity());
  }
}

TEST_F(CodedStreamTest, ReadStringNoReservationSizeIsOverTheLimit) {
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  // Buffer size in the input must be smaller than sizeof(kRawBytes),
  // otherwise check against capacity will fail as ReadStringInline()
  // will handle the reading and will reserve the memory as needed.
  ArrayInputStream input(buffer_, sizeof(buffer_), 32);

  {
    CodedInputStream coded_input(&input);
    coded_input.PushLimit(16);

    std::string str;
    EXPECT_FALSE(coded_input.ReadString(&str, strlen(kRawBytes)));
    // Note: this check depends on string class implementation. It
    // expects that string will allocate less than strlen(kRawBytes)
    // for an empty string.
    EXPECT_GT(strlen(kRawBytes), str.capacity());
  }
}

TEST_F(CodedStreamTest, ReadStringNoReservationSizeIsOverTheTotalBytesLimit) {
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  // Buffer size in the input must be smaller than sizeof(kRawBytes),
  // otherwise check against capacity will fail as ReadStringInline()
  // will handle the reading and will reserve the memory as needed.
  ArrayInputStream input(buffer_, sizeof(buffer_), 32);

  {
    CodedInputStream coded_input(&input);
    coded_input.SetTotalBytesLimit(16);

    std::string str;
    EXPECT_FALSE(coded_input.ReadString(&str, strlen(kRawBytes)));
    // Note: this check depends on string class implementation. It
    // expects that string will allocate less than strlen(kRawBytes)
    // for an empty string.
    EXPECT_GT(strlen(kRawBytes), str.capacity());
  }
}

TEST_F(CodedStreamTest,
       ReadStringNoReservationSizeIsOverTheClosestLimit_GlobalLimitIsCloser) {
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  // Buffer size in the input must be smaller than sizeof(kRawBytes),
  // otherwise check against capacity will fail as ReadStringInline()
  // will handle the reading and will reserve the memory as needed.
  ArrayInputStream input(buffer_, sizeof(buffer_), 32);

  {
    CodedInputStream coded_input(&input);
    coded_input.PushLimit(sizeof(buffer_));
    coded_input.SetTotalBytesLimit(16);

    std::string str;
    EXPECT_FALSE(coded_input.ReadString(&str, strlen(kRawBytes)));
    // Note: this check depends on string class implementation. It
    // expects that string will allocate less than strlen(kRawBytes)
    // for an empty string.
    EXPECT_GT(strlen(kRawBytes), str.capacity());
  }
}

TEST_F(CodedStreamTest,
       ReadStringNoReservationSizeIsOverTheClosestLimit_LocalLimitIsCloser) {
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  // Buffer size in the input must be smaller than sizeof(kRawBytes),
  // otherwise check against capacity will fail as ReadStringInline()
  // will handle the reading and will reserve the memory as needed.
  ArrayInputStream input(buffer_, sizeof(buffer_), 32);

  {
    CodedInputStream coded_input(&input);
    coded_input.PushLimit(16);
    coded_input.SetTotalBytesLimit(sizeof(buffer_));
    EXPECT_EQ(sizeof(buffer_), coded_input.BytesUntilTotalBytesLimit());

    std::string str;
    EXPECT_FALSE(coded_input.ReadString(&str, strlen(kRawBytes)));
    // Note: this check depends on string class implementation. It
    // expects that string will allocate less than strlen(kRawBytes)
    // for an empty string.
    EXPECT_GT(strlen(kRawBytes), str.capacity());
  }
}


// -------------------------------------------------------------------
// Cord reads and writes

class BlockSizesWithResetCords
    : public CodedStreamTest,
      public testing::WithParamInterface<std::tuple<int, bool>> {};

class ResetCords : public CodedStreamTest,
                   public testing::WithParamInterface<bool> {};

TEST_P(BlockSizes, ReadCord) {
  int kBlockSizes_case = GetParam();
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    absl::Cord cord;
    EXPECT_TRUE(coded_input.ReadCord(&cord, strlen(kRawBytes)));
    EXPECT_EQ(absl::Cord(kRawBytes), cord);
  }

  EXPECT_EQ(strlen(kRawBytes), input.ByteCount());
}

TEST_P(BlockSizes, ReadCordReuseCord) {
  ASSERT_GT(sizeof(buffer_), 1362 * sizeof(kRawBytes));
  int kBlockSizes_case = GetParam();
  for (size_t i = 0; i < 1362; i++) {
    memcpy(buffer_ + i * sizeof(kRawBytes), kRawBytes, sizeof(kRawBytes));
  }
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  int total_read = 0;
  {
    const char* buffer_str = reinterpret_cast<const char*>(buffer_);
    CodedInputStream coded_input(&input);
    static const int kSizes[] = {0,  1,  2,  3,   4,    5,    6,    7,
                                 8,  9,  10, 11,  12,   13,   14,   15,
                                 16, 17, 50, 100, 1023, 1024, 8000, 16000};
    int total_size = 0;
    std::vector<int> sizes;
    for (size_t i = 0; i < ABSL_ARRAYSIZE(kSizes); ++i) {
      sizes.push_back(kSizes[i]);
      total_size += kSizes[i];
    }
    ASSERT_GE(1362 * sizeof(kRawBytes), total_size * 2);

    absl::Cord reused_cord;
    for (int pass = 0; pass < 2; ++pass) {
      for (size_t i = 0; i < sizes.size(); ++i) {
        const int size = sizes[i];
        EXPECT_TRUE(coded_input.ReadCord(&reused_cord, size));
        EXPECT_EQ(size, reused_cord.size());
        EXPECT_EQ(
            std::string(buffer_str + total_read, static_cast<size_t>(size)),
            std::string(reused_cord));
        total_read += size;
      }
      std::reverse(sizes.begin(), sizes.end());  // Second pass is in reverse.
    }
  }
  EXPECT_EQ(total_read, input.ByteCount());
}

TEST_P(BlockSizesWithResetCords, ReadCordWithLimit) {
  int kBlockSizes_case = std::get<0>(GetParam());
  bool kResetCords_case = std::get<1>(GetParam());
  memcpy(buffer_, kRawBytes, strlen(kRawBytes));
  ArrayInputStream input(buffer_, strlen(kRawBytes), kBlockSizes_case);
  CodedInputStream coded_input(&input);

  CodedInputStream::Limit limit = coded_input.PushLimit(10);
  absl::Cord cord;
  EXPECT_TRUE(coded_input.ReadCord(&cord, 5));
  EXPECT_EQ(5, coded_input.BytesUntilLimit());
  if (kResetCords_case) cord.Clear();
  EXPECT_TRUE(coded_input.ReadCord(&cord, 4));
  EXPECT_EQ(1, coded_input.BytesUntilLimit());
  if (kResetCords_case) cord.Clear();
  EXPECT_FALSE(coded_input.ReadCord(&cord, 2));
  EXPECT_EQ(0, coded_input.BytesUntilLimit());
  EXPECT_EQ(1, cord.size());

  coded_input.PopLimit(limit);

  if (kResetCords_case) cord.Clear();
  EXPECT_TRUE(coded_input.ReadCord(&cord, strlen(kRawBytes) - 10));
  EXPECT_EQ(std::string(kRawBytes + 10), std::string(cord));
}

TEST_P(ResetCords, ReadLargeCord) {
  bool kResetCords_case = GetParam();
  absl::Cord large_cord;
  for (int i = 0; i < 1024; i++) {
    large_cord.Append(kRawBytes);
  }
  CordInputStream input(&large_cord);

  {
    CodedInputStream coded_input(&input);

    absl::Cord cord;
    if (!kResetCords_case) cord.Append(absl::string_view("value"));
    EXPECT_TRUE(
        coded_input.ReadCord(&cord, static_cast<int>(large_cord.size())));
    EXPECT_EQ(large_cord, cord);
  }

  EXPECT_EQ(large_cord.size(), input.ByteCount());
}

// Check to make sure ReadString doesn't crash on impossibly large strings.
TEST_P(BlockSizesWithResetCords, ReadCordImpossiblyLarge) {
  int kBlockSizes_case = std::get<0>(GetParam());
  bool kResetCords_case = std::get<1>(GetParam());
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    absl::Cord cord;
    if (!kResetCords_case) cord.Append(absl::string_view("value"));
    // Try to read a gigabyte.  This should fail because the input is only
    // sizeof(buffer_) bytes.
    EXPECT_FALSE(coded_input.ReadCord(&cord, 1 << 30));
  }
}

TEST_P(BlockSizes, WriteCord) {
  int kBlockSizes_case = GetParam();
  ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedOutputStream coded_output(&output);

    absl::Cord cord(kRawBytes);
    coded_output.WriteCord(cord);
    EXPECT_FALSE(coded_output.HadError());

    EXPECT_EQ(strlen(kRawBytes), coded_output.ByteCount());
  }

  EXPECT_EQ(strlen(kRawBytes), output.ByteCount());
  EXPECT_EQ(0, memcmp(buffer_, kRawBytes, strlen(kRawBytes)));
}

TEST_F(CodedStreamTest, WriteLargeCord) {
  absl::Cord large_cord;
  for (int i = 0; i < 1024; i++) {
    large_cord.Append(kRawBytes);
  }

  CordOutputStream output;
  {
    CodedOutputStream coded_output(&output);

    coded_output.WriteCord(large_cord);
    EXPECT_FALSE(coded_output.HadError());

    EXPECT_EQ(large_cord.size(), coded_output.ByteCount());
    EXPECT_EQ(large_cord.size(), output.ByteCount());
  }
  absl::Cord output_cord = output.Consume();
  EXPECT_EQ(large_cord, output_cord);
}

TEST_F(CodedStreamTest, Trim) {
  CordOutputStream cord_output;
  CodedOutputStream coded_output(&cord_output);

  // Verify that any initially reserved output buffers created when the output
  // streams were created are trimmed on an initial Trim call.
  coded_output.Trim();
  EXPECT_EQ(0, coded_output.ByteCount());

  // Write a single byte to the coded stream, ensure the cord stream has been
  // advanced, and then verify Trim() does the right thing.
  const char kTestData[] = "abcdef";
  coded_output.WriteRaw(kTestData, 1);
  coded_output.Trim();
  EXPECT_EQ(1, coded_output.ByteCount());

  // Write some more data to the coded stream, Trim() it, and verify
  // everything behaves as expected.
  coded_output.WriteRaw(kTestData, sizeof(kTestData));
  coded_output.Trim();
  EXPECT_EQ(1 + sizeof(kTestData), coded_output.ByteCount());

  absl::Cord cord = cord_output.Consume();
  EXPECT_EQ(1 + sizeof(kTestData), cord.size());
}


// -------------------------------------------------------------------
// Skip

const char kSkipTestBytes[] =
    "<Before skipping><To be skipped><After skipping>";

TEST_P(BlockSizes, SkipInput) {
  int kBlockSizes_case = GetParam();
  memcpy(buffer_, kSkipTestBytes, sizeof(kSkipTestBytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    std::string str;
    EXPECT_TRUE(coded_input.ReadString(&str, strlen("<Before skipping>")));
    EXPECT_EQ("<Before skipping>", str);
    EXPECT_TRUE(coded_input.Skip(strlen("<To be skipped>")));
    EXPECT_TRUE(coded_input.ReadString(&str, strlen("<After skipping>")));
    EXPECT_EQ("<After skipping>", str);
  }

  EXPECT_EQ(strlen(kSkipTestBytes), input.ByteCount());
}

// -------------------------------------------------------------------
// GetDirectBufferPointer

TEST_F(CodedStreamTest, GetDirectBufferPointerInput) {
  ArrayInputStream input(buffer_, sizeof(buffer_), 8);
  CodedInputStream coded_input(&input);

  const void* ptr;
  int size;

  EXPECT_TRUE(coded_input.GetDirectBufferPointer(&ptr, &size));
  EXPECT_EQ(buffer_, ptr);
  EXPECT_EQ(8, size);

  // Peeking again should return the same pointer.
  EXPECT_TRUE(coded_input.GetDirectBufferPointer(&ptr, &size));
  EXPECT_EQ(buffer_, ptr);
  EXPECT_EQ(8, size);

  // Skip forward in the same buffer then peek again.
  EXPECT_TRUE(coded_input.Skip(3));
  EXPECT_TRUE(coded_input.GetDirectBufferPointer(&ptr, &size));
  EXPECT_EQ(buffer_ + 3, ptr);
  EXPECT_EQ(5, size);

  // Skip to end of buffer and peek -- should get next buffer.
  EXPECT_TRUE(coded_input.Skip(5));
  EXPECT_TRUE(coded_input.GetDirectBufferPointer(&ptr, &size));
  EXPECT_EQ(buffer_ + 8, ptr);
  EXPECT_EQ(8, size);
}

TEST_F(CodedStreamTest, GetDirectBufferPointerInlineInput) {
  ArrayInputStream input(buffer_, sizeof(buffer_), 8);
  CodedInputStream coded_input(&input);

  const void* ptr;
  int size;

  coded_input.GetDirectBufferPointerInline(&ptr, &size);
  EXPECT_EQ(buffer_, ptr);
  EXPECT_EQ(8, size);

  // Peeking again should return the same pointer.
  coded_input.GetDirectBufferPointerInline(&ptr, &size);
  EXPECT_EQ(buffer_, ptr);
  EXPECT_EQ(8, size);

  // Skip forward in the same buffer then peek again.
  EXPECT_TRUE(coded_input.Skip(3));
  coded_input.GetDirectBufferPointerInline(&ptr, &size);
  EXPECT_EQ(buffer_ + 3, ptr);
  EXPECT_EQ(5, size);

  // Skip to end of buffer and peek -- should return false and provide an empty
  // buffer. It does not try to Refresh().
  EXPECT_TRUE(coded_input.Skip(5));
  coded_input.GetDirectBufferPointerInline(&ptr, &size);
  EXPECT_EQ(buffer_ + 8, ptr);
  EXPECT_EQ(0, size);
}

// -------------------------------------------------------------------
// Limits

TEST_P(BlockSizes, BasicLimit) {
  int kBlockSizes_case = GetParam();
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    EXPECT_EQ(-1, coded_input.BytesUntilLimit());
    CodedInputStream::Limit limit = coded_input.PushLimit(8);

    // Read until we hit the limit.
    uint32_t value;
    EXPECT_EQ(8, coded_input.BytesUntilLimit());
    EXPECT_TRUE(coded_input.ReadLittleEndian32(&value));
    EXPECT_EQ(4, coded_input.BytesUntilLimit());
    EXPECT_TRUE(coded_input.ReadLittleEndian32(&value));
    EXPECT_EQ(0, coded_input.BytesUntilLimit());
    EXPECT_FALSE(coded_input.ReadLittleEndian32(&value));
    EXPECT_EQ(0, coded_input.BytesUntilLimit());

    coded_input.PopLimit(limit);

    EXPECT_EQ(-1, coded_input.BytesUntilLimit());
    EXPECT_TRUE(coded_input.ReadLittleEndian32(&value));
  }

  EXPECT_EQ(12, input.ByteCount());
}

// Test what happens when we push two limits where the second (top) one is
// shorter.
TEST_P(BlockSizes, SmallLimitOnTopOfBigLimit) {
  int kBlockSizes_case = GetParam();
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    EXPECT_EQ(-1, coded_input.BytesUntilLimit());
    CodedInputStream::Limit limit1 = coded_input.PushLimit(8);
    EXPECT_EQ(8, coded_input.BytesUntilLimit());
    CodedInputStream::Limit limit2 = coded_input.PushLimit(4);

    uint32_t value;

    // Read until we hit limit2, the top and shortest limit.
    EXPECT_EQ(4, coded_input.BytesUntilLimit());
    EXPECT_TRUE(coded_input.ReadLittleEndian32(&value));
    EXPECT_EQ(0, coded_input.BytesUntilLimit());
    EXPECT_FALSE(coded_input.ReadLittleEndian32(&value));
    EXPECT_EQ(0, coded_input.BytesUntilLimit());

    coded_input.PopLimit(limit2);

    // Read until we hit limit1.
    EXPECT_EQ(4, coded_input.BytesUntilLimit());
    EXPECT_TRUE(coded_input.ReadLittleEndian32(&value));
    EXPECT_EQ(0, coded_input.BytesUntilLimit());
    EXPECT_FALSE(coded_input.ReadLittleEndian32(&value));
    EXPECT_EQ(0, coded_input.BytesUntilLimit());

    coded_input.PopLimit(limit1);

    // No more limits.
    EXPECT_EQ(-1, coded_input.BytesUntilLimit());
    EXPECT_TRUE(coded_input.ReadLittleEndian32(&value));
  }

  EXPECT_EQ(12, input.ByteCount());
}

// Test what happens when we push two limits where the second (top) one is
// longer.  In this case, the top limit is shortened to match the previous
// limit.
TEST_P(BlockSizes, BigLimitOnTopOfSmallLimit) {
  int kBlockSizes_case = GetParam();
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    EXPECT_EQ(-1, coded_input.BytesUntilLimit());
    CodedInputStream::Limit limit1 = coded_input.PushLimit(4);
    EXPECT_EQ(4, coded_input.BytesUntilLimit());
    CodedInputStream::Limit limit2 = coded_input.PushLimit(8);

    uint32_t value;

    // Read until we hit limit2.  Except, wait!  limit1 is shorter, so
    // we end up hitting that first, despite having 4 bytes to go on
    // limit2.
    EXPECT_EQ(4, coded_input.BytesUntilLimit());
    EXPECT_TRUE(coded_input.ReadLittleEndian32(&value));
    EXPECT_EQ(0, coded_input.BytesUntilLimit());
    EXPECT_FALSE(coded_input.ReadLittleEndian32(&value));
    EXPECT_EQ(0, coded_input.BytesUntilLimit());

    coded_input.PopLimit(limit2);

    // OK, popped limit2, now limit1 is on top, which we've already hit.
    EXPECT_EQ(0, coded_input.BytesUntilLimit());
    EXPECT_FALSE(coded_input.ReadLittleEndian32(&value));
    EXPECT_EQ(0, coded_input.BytesUntilLimit());

    coded_input.PopLimit(limit1);

    // No more limits.
    EXPECT_EQ(-1, coded_input.BytesUntilLimit());
    EXPECT_TRUE(coded_input.ReadLittleEndian32(&value));
  }

  EXPECT_EQ(8, input.ByteCount());
}

TEST_F(CodedStreamTest, ExpectAtEnd) {
  // Test ExpectAtEnd(), which is based on limits.
  ArrayInputStream input(buffer_, sizeof(buffer_));
  CodedInputStream coded_input(&input);

  EXPECT_FALSE(coded_input.ExpectAtEnd());

  CodedInputStream::Limit limit = coded_input.PushLimit(4);

  uint32_t value;
  EXPECT_TRUE(coded_input.ReadLittleEndian32(&value));
  EXPECT_TRUE(coded_input.ExpectAtEnd());

  coded_input.PopLimit(limit);
  EXPECT_FALSE(coded_input.ExpectAtEnd());
}

TEST_F(CodedStreamTest, NegativeLimit) {
  // Check what happens when we push a negative limit.
  ArrayInputStream input(buffer_, sizeof(buffer_));
  CodedInputStream coded_input(&input);

  CodedInputStream::Limit limit = coded_input.PushLimit(-1234);
  // BytesUntilLimit() returns -1 to mean "no limit", which actually means
  // "the limit is INT_MAX relative to the beginning of the stream".
  EXPECT_EQ(-1, coded_input.BytesUntilLimit());
  coded_input.PopLimit(limit);
}

TEST_F(CodedStreamTest, NegativeLimitAfterReading) {
  // Check what happens when we push a negative limit.
  ArrayInputStream input(buffer_, sizeof(buffer_));
  CodedInputStream coded_input(&input);
  ASSERT_TRUE(coded_input.Skip(128));

  CodedInputStream::Limit limit = coded_input.PushLimit(-64);
  // BytesUntilLimit() returns -1 to mean "no limit", which actually means
  // "the limit is INT_MAX relative to the beginning of the stream".
  EXPECT_EQ(-1, coded_input.BytesUntilLimit());
  coded_input.PopLimit(limit);
}

TEST_F(CodedStreamTest, OverflowLimit) {
  // Check what happens when we push a limit large enough that its absolute
  // position is more than 2GB into the stream.
  ArrayInputStream input(buffer_, sizeof(buffer_));
  CodedInputStream coded_input(&input);
  ASSERT_TRUE(coded_input.Skip(128));

  CodedInputStream::Limit limit = coded_input.PushLimit(INT_MAX);
  // BytesUntilLimit() returns -1 to mean "no limit", which actually means
  // "the limit is INT_MAX relative to the beginning of the stream".
  EXPECT_EQ(-1, coded_input.BytesUntilLimit());
  coded_input.PopLimit(limit);
}

TEST_F(CodedStreamTest, TotalBytesLimit) {
  ArrayInputStream input(buffer_, sizeof(buffer_));
  CodedInputStream coded_input(&input);
  coded_input.SetTotalBytesLimit(16);
  EXPECT_EQ(16, coded_input.BytesUntilTotalBytesLimit());

  std::string str;
  EXPECT_TRUE(coded_input.ReadString(&str, 16));
  EXPECT_EQ(0, coded_input.BytesUntilTotalBytesLimit());

  {
    absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
    EXPECT_CALL(
        log,
        Log(absl::LogSeverity::kError, testing::_,
            testing::HasSubstr(
                "A protocol message was rejected because it was too big")));
    log.StartCapturingLogs();
    EXPECT_FALSE(coded_input.ReadString(&str, 1));
  }

  coded_input.SetTotalBytesLimit(32);
  EXPECT_EQ(16, coded_input.BytesUntilTotalBytesLimit());
  EXPECT_TRUE(coded_input.ReadString(&str, 16));
  EXPECT_EQ(0, coded_input.BytesUntilTotalBytesLimit());
}

TEST_F(CodedStreamTest, TotalBytesLimitNotValidMessageEnd) {
  // total_bytes_limit_ is not a valid place for a message to end.

  ArrayInputStream input(buffer_, sizeof(buffer_));
  CodedInputStream coded_input(&input);

  // Set both total_bytes_limit and a regular limit at 16 bytes.
  coded_input.SetTotalBytesLimit(16);
  CodedInputStream::Limit limit = coded_input.PushLimit(16);

  // Read 16 bytes.
  std::string str;
  EXPECT_TRUE(coded_input.ReadString(&str, 16));

  // Read a tag.  Should fail, but report being a valid endpoint since it's
  // a regular limit.
  EXPECT_EQ(0, coded_input.ReadTagNoLastTag());
  EXPECT_TRUE(coded_input.ConsumedEntireMessage());

  // Pop the limit.
  coded_input.PopLimit(limit);

  // Read a tag.  Should fail, and report *not* being a valid endpoint, since
  // this time we're hitting the total bytes limit.
  EXPECT_EQ(0, coded_input.ReadTagNoLastTag());
  EXPECT_FALSE(coded_input.ConsumedEntireMessage());
}

TEST_F(CodedStreamTest, RecursionLimit) {
  ArrayInputStream input(buffer_, sizeof(buffer_));
  CodedInputStream coded_input(&input);
  coded_input.SetRecursionLimit(4);

  // This is way too much testing for a counter.
  EXPECT_TRUE(coded_input.IncrementRecursionDepth());   // 1
  EXPECT_TRUE(coded_input.IncrementRecursionDepth());   // 2
  EXPECT_TRUE(coded_input.IncrementRecursionDepth());   // 3
  EXPECT_TRUE(coded_input.IncrementRecursionDepth());   // 4
  EXPECT_FALSE(coded_input.IncrementRecursionDepth());  // 5
  EXPECT_FALSE(coded_input.IncrementRecursionDepth());  // 6
  coded_input.DecrementRecursionDepth();                // 5
  EXPECT_FALSE(coded_input.IncrementRecursionDepth());  // 6
  coded_input.DecrementRecursionDepth();                // 5
  coded_input.DecrementRecursionDepth();                // 4
  coded_input.DecrementRecursionDepth();                // 3
  EXPECT_TRUE(coded_input.IncrementRecursionDepth());   // 4
  EXPECT_FALSE(coded_input.IncrementRecursionDepth());  // 5
  coded_input.DecrementRecursionDepth();                // 4
  coded_input.DecrementRecursionDepth();                // 3
  coded_input.DecrementRecursionDepth();                // 2
  coded_input.DecrementRecursionDepth();                // 1
  coded_input.DecrementRecursionDepth();                // 0
  coded_input.DecrementRecursionDepth();                // 0
  coded_input.DecrementRecursionDepth();                // 0
  EXPECT_TRUE(coded_input.IncrementRecursionDepth());   // 1
  EXPECT_TRUE(coded_input.IncrementRecursionDepth());   // 2
  EXPECT_TRUE(coded_input.IncrementRecursionDepth());   // 3
  EXPECT_TRUE(coded_input.IncrementRecursionDepth());   // 4
  EXPECT_FALSE(coded_input.IncrementRecursionDepth());  // 5

  coded_input.SetRecursionLimit(6);
  EXPECT_TRUE(coded_input.IncrementRecursionDepth());   // 6
  EXPECT_FALSE(coded_input.IncrementRecursionDepth());  // 7
}


class ReallyBigInputStream : public ZeroCopyInputStream {
 public:
  ReallyBigInputStream() : backup_amount_(0), buffer_count_(0) {}

  // implements ZeroCopyInputStream ----------------------------------
  bool Next(const void** data, int* size) override {
    // We only expect BackUp() to be called at the end.
    EXPECT_EQ(0, backup_amount_);

    switch (buffer_count_++) {
      case 0:
        *data = buffer_;
        *size = sizeof(buffer_);
        return true;
      case 1:
        // Return an enormously large buffer that, when combined with the 1k
        // returned already, should overflow the total_bytes_read_ counter in
        // CodedInputStream.  Note that we'll only read the first 1024 bytes
        // of this buffer so it's OK that we have it point at buffer_.
        *data = buffer_;
        *size = INT_MAX;
        return true;
      default:
        return false;
    }
  }

  void BackUp(int count) override { backup_amount_ = count; }

  bool Skip(int count) override {
    ABSL_LOG(FATAL) << "Not implemented.";
    return false;
  }
  int64_t ByteCount() const override {
    ABSL_LOG(FATAL) << "Not implemented.";
    return 0;
  }

  int backup_amount_;

 private:
  char buffer_[1024] = {};
  int64_t buffer_count_;
};

TEST_F(CodedStreamTest, InputOver2G) {
  // CodedInputStream should gracefully handle input over 2G and call
  // input.BackUp() with the correct number of bytes on destruction.
  ReallyBigInputStream input;

  {
    absl::ScopedMockLog log(absl::MockLogDefault::kDisallowUnexpected);
    EXPECT_CALL(log, Log(absl::LogSeverity::kError, testing::_, testing::_))
        .Times(0);
    log.StartCapturingLogs();
    CodedInputStream coded_input(&input);
    std::string str;
    EXPECT_TRUE(coded_input.ReadString(&str, 512));
    EXPECT_TRUE(coded_input.ReadString(&str, 1024));
  }

  EXPECT_EQ(INT_MAX - 512, input.backup_amount_);
}

INSTANTIATE_TEST_SUITE_P(
    CodedStreamUnitTest, ResetCords, testing::ValuesIn(kResetCords),
    [](const testing::TestParamInfo<ResetCords::ParamType>& param_info) {
      return absl::StrCat("ResetCords_", param_info.param ? "true" : "false");
    });
INSTANTIATE_TEST_SUITE_P(
    CodedStreamUnitTest, BlockSizesWithResetCords,
    testing::Combine(testing::ValuesIn(kBlockSizes),
                     testing::ValuesIn(kResetCords)),
    [](const testing::TestParamInfo<BlockSizesWithResetCords::ParamType>&
           param_info) {
      return absl::StrCat("BlockSize_", std::get<0>(param_info.param),
                          "_ResetCords_",
                          std::get<1>(param_info.param) ? "true" : "false");
    });
INSTANTIATE_TEST_SUITE_P(
    CodedStreamUnitTest, VarintErrorCasesWithSizes,
    testing::Combine(testing::ValuesIn(kVarintErrorCases),
                     testing::ValuesIn(kBlockSizes)),
    [](const testing::TestParamInfo<VarintErrorCasesWithSizes::ParamType>&
           param_info) {
      return absl::StrCat("VarintErrorCase_",
                          std::get<0>(param_info.param).name, "_BlockSize_",
                          std::get<1>(param_info.param));
    });
INSTANTIATE_TEST_SUITE_P(
    CodedStreamUnitTest, VarintSizeCases, testing::ValuesIn(kVarintSizeCases),
    [](const testing::TestParamInfo<VarintSizeCases::ParamType>& param_info) {
      return absl::StrCat("VarintSizeCase_Value_", param_info.param.value);
    });
INSTANTIATE_TEST_SUITE_P(
    CodedStreamUnitTest, VarintCases, testing::ValuesIn(kVarintCases),
    [](const testing::TestParamInfo<VarintCases::ParamType>& param_info) {
      return absl::StrCat("VarintCase_Value_", param_info.param.value);
    });
INSTANTIATE_TEST_SUITE_P(
    CodedStreamUnitTest, VarintCasesWithSizes,
    testing::Combine(testing::ValuesIn(kVarintCases),
                     testing::ValuesIn(kBlockSizes)),
    [](const testing::TestParamInfo<VarintCasesWithSizes::ParamType>&
           param_info) {
      return absl::StrCat("VarintCase_Value_",
                          std::get<0>(param_info.param).value, "_BlockSize_",
                          std::get<1>(param_info.param));
    });
INSTANTIATE_TEST_SUITE_P(
    CodedStreamUnitTest, BlockSizes, testing::ValuesIn(kBlockSizes),
    [](const testing::TestParamInfo<BlockSizes::ParamType>& param_info) {
      return absl::StrCat("BlockSize_", param_info.param);
    });
INSTANTIATE_TEST_SUITE_P(
    CodedStreamUnitTest, SignedVarintCasesWithSizes,
    testing::Combine(testing::ValuesIn(kSignExtendedVarintCases),
                     testing::ValuesIn(kBlockSizes)),
    [](const testing::TestParamInfo<SignedVarintCasesWithSizes::ParamType>&
           param_info) {
      return absl::StrCat("SignedVarintCase_Value_",
                          std::get<0>(param_info.param) < 0 ? "_Negative_" : "",
                          std::abs(std::get<0>(param_info.param)),
                          "_BlockSize_", std::get<1>(param_info.param));
    });
INSTANTIATE_TEST_SUITE_P(
    CodedStreamUnitTest, Fixed16Cases, testing::ValuesIn(kFixed16Cases),
    [](const testing::TestParamInfo<Fixed16Cases::ParamType>& param_info) {
      return absl::StrCat("Fixed16Case_Value_", param_info.param.value);
    });
INSTANTIATE_TEST_SUITE_P(
    CodedStreamUnitTest, Fixed32Cases, testing::ValuesIn(kFixed32Cases),
    [](const testing::TestParamInfo<Fixed32Cases::ParamType>& param_info) {
      return absl::StrCat("Fixed32Case_Value_", param_info.param.value);
    });
INSTANTIATE_TEST_SUITE_P(
    CodedStreamUnitTest, Fixed64Cases, testing::ValuesIn(kFixed64Cases),
    [](const testing::TestParamInfo<Fixed64Cases::ParamType>& param_info) {
      return absl::StrCat("Fixed64Case_Value_", param_info.param.value);
    });
INSTANTIATE_TEST_SUITE_P(
    CodedStreamUnitTest, Fixed16CasesWithSizes,
    testing::Combine(testing::ValuesIn(kFixed16Cases),
                     testing::ValuesIn(kBlockSizes)),
    [](const testing::TestParamInfo<Fixed16CasesWithSizes::ParamType>&
           param_info) {
      return absl::StrCat("Fixed16Case_Value_",
                          std::get<0>(param_info.param).value, "_BlockSize_",
                          std::get<1>(param_info.param));
    });
INSTANTIATE_TEST_SUITE_P(
    CodedStreamUnitTest, Fixed32CasesWithSizes,
    testing::Combine(testing::ValuesIn(kFixed32Cases),
                     testing::ValuesIn(kBlockSizes)),
    [](const testing::TestParamInfo<Fixed32CasesWithSizes::ParamType>&
           param_info) {
      return absl::StrCat("Fixed32Case_Value_",
                          std::get<0>(param_info.param).value, "_BlockSize_",
                          std::get<1>(param_info.param));
    });
INSTANTIATE_TEST_SUITE_P(
    CodedStreamUnitTest, Fixed64CasesWithSizes,
    testing::Combine(testing::ValuesIn(kFixed64Cases),
                     testing::ValuesIn(kBlockSizes)),
    [](const testing::TestParamInfo<Fixed64CasesWithSizes::ParamType>&
           param_info) {
      return absl::StrCat("Fixed64Case_Value_",
                          std::get<0>(param_info.param).value, "_BlockSize_",
                          std::get<1>(param_info.param));
    });

}  // namespace
}  // namespace io
}  // namespace protobuf
}  // namespace google

#include "google/protobuf/port_undef.inc"
