text:
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

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

#include <vector>

#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/descriptor.pb.h>

#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>

namespace google {
namespace protobuf {
namespace io {
namespace {

// Each test repeats over several block sizes in order to test both cases
// where particular writes cross a buffer boundary and cases where they do
// not.

TEST(Printer, EmptyPrinter) {
  char buffer[8192];
  const int block_size = 100;
  ArrayOutputStream output(buffer, GOOGLE_ARRAYSIZE(buffer), block_size);
  Printer printer(&output, '\0');
  EXPECT_TRUE(!printer.failed());
}

TEST(Printer, BasicPrinting) {
  char buffer[8192];

  for (int block_size = 1; block_size < 512; block_size *= 2) {
    ArrayOutputStream output(buffer, sizeof(buffer), block_size);

    {
      Printer printer(&output, '\0');

      printer.Print("Hello World!");
      printer.Print("  This is the same line.\n");
      printer.Print("But this is a new one.\nAnd this is another one.");

      EXPECT_FALSE(printer.failed());
    }

    buffer[output.ByteCount()] = '\0';

    EXPECT_STREQ("Hello World!  This is the same line.\n"
                 "But this is a new one.\n"
                 "And this is another one.",
                 buffer);
  }
}

TEST(Printer, WriteRaw) {
  char buffer[8192];

  for (int block_size = 1; block_size < 512; block_size *= 2) {
    ArrayOutputStream output(buffer, sizeof(buffer), block_size);

    {
      string string_obj = "From an object\n";
      Printer printer(&output, '$');
      printer.WriteRaw("Hello World!", 12);
      printer.PrintRaw("  This is the same line.\n");
      printer.PrintRaw("But this is a new one.\nAnd this is another one.");
      printer.WriteRaw("\n", 1);
      printer.PrintRaw(string_obj);
      EXPECT_FALSE(printer.failed());
    }

    buffer[output.ByteCount()] = '\0';

    EXPECT_STREQ("Hello World!  This is the same line.\n"
                 "But this is a new one.\n"
                 "And this is another one."
                 "\n"
                 "From an object\n",
                 buffer);
  }
}

TEST(Printer, VariableSubstitution) {
  char buffer[8192];

  for (int block_size = 1; block_size < 512; block_size *= 2) {
    ArrayOutputStream output(buffer, sizeof(buffer), block_size);

    {
      Printer printer(&output, '$');
      std::map<string, string> vars;

      vars["foo"] = "World";
      vars["bar"] = "$foo$";
      vars["abcdefg"] = "1234";

      printer.Print(vars, "Hello $foo$!\nbar = $bar$\n");
      printer.PrintRaw("RawBit\n");
      printer.Print(vars, "$abcdefg$\nA literal dollar sign:  $$");

      vars["foo"] = "blah";
      printer.Print(vars, "\nNow foo = $foo$.");

      EXPECT_FALSE(printer.failed());
    }

    buffer[output.ByteCount()] = '\0';

    EXPECT_STREQ("Hello World!\n"
                 "bar = $foo$\n"
                 "RawBit\n"
                 "1234\n"
                 "A literal dollar sign:  $\n"
                 "Now foo = blah.",
                 buffer);
  }
}

TEST(Printer, InlineVariableSubstitution) {
  char buffer[8192];

  ArrayOutputStream output(buffer, sizeof(buffer));

  {
    Printer printer(&output, '$');
    printer.Print("Hello $foo$!\n", "foo", "World");
    printer.PrintRaw("RawBit\n");
    printer.Print("$foo$ $bar$\n", "foo", "one", "bar", "two");
    EXPECT_FALSE(printer.failed());
  }

  buffer[output.ByteCount()] = '\0';

  EXPECT_STREQ("Hello World!\n"
               "RawBit\n"
               "one two\n",
               buffer);
}

// MockDescriptorFile defines only those members that Printer uses to write out
// annotations.
class MockDescriptorFile {
 public:
  explicit MockDescriptorFile(const string& file) : file_(file) {}

  // The mock filename for this file.
  const string& name() const { return file_; }

 private:
  string file_;
};

// MockDescriptor defines only those members that Printer uses to write out
// annotations.
class MockDescriptor {
 public:
  MockDescriptor(const string& file, const std::vector<int>& path)
      : file_(file), path_(path) {}

  // The mock file in which this descriptor was defined.
  const MockDescriptorFile* file() const { return &file_; }

 private:
  // Allows access to GetLocationPath.
  friend class ::google::protobuf::io::Printer;

  // Copies the pre-stored path to output.
  void GetLocationPath(std::vector<int>* output) const { *output = path_; }

  MockDescriptorFile file_;
  std::vector<int> path_;
};

TEST(Printer, AnnotateMap) {
  char buffer[8192];
  ArrayOutputStream output(buffer, sizeof(buffer));
  GeneratedCodeInfo info;
  AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
  {
    Printer printer(&output, '$', &info_collector);
    std::map<string, string> vars;
    vars["foo"] = "3";
    vars["bar"] = "5";
    printer.Print(vars, "012$foo$4$bar$\n");
    std::vector<int> path_1;
    path_1.push_back(33);
    std::vector<int> path_2;
    path_2.push_back(11);
    path_2.push_back(22);
    MockDescriptor descriptor_1("path_1", path_1);
    MockDescriptor descriptor_2("path_2", path_2);
    printer.Annotate("foo", "foo", &descriptor_1);
    printer.Annotate("bar", "bar", &descriptor_2);
  }
  buffer[output.ByteCount()] = '\0';
  EXPECT_STREQ("012345\n", buffer);
  ASSERT_EQ(2, info.annotation_size());
  const GeneratedCodeInfo::Annotation* foo = info.annotation(0).path_size() == 1
                                                 ? &info.annotation(0)
                                                 : &info.annotation(1);
  const GeneratedCodeInfo::Annotation* bar = info.annotation(0).path_size() == 1
                                                 ? &info.annotation(1)
                                                 : &info.annotation(0);
  ASSERT_EQ(1, foo->path_size());
  ASSERT_EQ(2, bar->path_size());
  EXPECT_EQ(33, foo->path(0));
  EXPECT_EQ(11, bar->path(0));
  EXPECT_EQ(22, bar->path(1));
  EXPECT_EQ("path_1", foo->source_file());
  EXPECT_EQ("path_2", bar->source_file());
  EXPECT_EQ(3, foo->begin());
  EXPECT_EQ(4, foo->end());
  EXPECT_EQ(5, bar->begin());
  EXPECT_EQ(6, bar->end());
}

TEST(Printer, AnnotateInline) {
  char buffer[8192];
  ArrayOutputStream output(buffer, sizeof(buffer));
  GeneratedCodeInfo info;
  AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
  {
    Printer printer(&output, '$', &info_collector);
    printer.Print("012$foo$4$bar$\n", "foo", "3", "bar", "5");
    std::vector<int> path_1;
    path_1.push_back(33);
    std::vector<int> path_2;
    path_2.push_back(11);
    path_2.push_back(22);
    MockDescriptor descriptor_1("path_1", path_1);
    MockDescriptor descriptor_2("path_2", path_2);
    printer.Annotate("foo", "foo", &descriptor_1);
    printer.Annotate("bar", "bar", &descriptor_2);
  }
  buffer[output.ByteCount()] = '\0';
  EXPECT_STREQ("012345\n", buffer);
  ASSERT_EQ(2, info.annotation_size());
  const GeneratedCodeInfo::Annotation* foo = info.annotation(0).path_size() == 1
                                                 ? &info.annotation(0)
                                                 : &info.annotation(1);
  const GeneratedCodeInfo::Annotation* bar = info.annotation(0).path_size() == 1
                                                 ? &info.annotation(1)
                                                 : &info.annotation(0);
  ASSERT_EQ(1, foo->path_size());
  ASSERT_EQ(2, bar->path_size());
  EXPECT_EQ(33, foo->path(0));
  EXPECT_EQ(11, bar->path(0));
  EXPECT_EQ(22, bar->path(1));
  EXPECT_EQ("path_1", foo->source_file());
  EXPECT_EQ("path_2", bar->source_file());
  EXPECT_EQ(3, foo->begin());
  EXPECT_EQ(4, foo->end());
  EXPECT_EQ(5, bar->begin());
  EXPECT_EQ(6, bar->end());
}

TEST(Printer, AnnotateRange) {
  char buffer[8192];
  ArrayOutputStream output(buffer, sizeof(buffer));
  GeneratedCodeInfo info;
  AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
  {
    Printer printer(&output, '$', &info_collector);
    printer.Print("012$foo$4$bar$\n", "foo", "3", "bar", "5");
    std::vector<int> path;
    path.push_back(33);
    MockDescriptor descriptor("path", path);
    printer.Annotate("foo", "bar", &descriptor);
  }
  buffer[output.ByteCount()] = '\0';
  EXPECT_STREQ("012345\n", buffer);
  ASSERT_EQ(1, info.annotation_size());
  const GeneratedCodeInfo::Annotation* foobar = &info.annotation(0);
  ASSERT_EQ(1, foobar->path_size());
  EXPECT_EQ(33, foobar->path(0));
  EXPECT_EQ("path", foobar->source_file());
  EXPECT_EQ(3, foobar->begin());
  EXPECT_EQ(6, foobar->end());
}

TEST(Printer, AnnotateEmptyRange) {
  char buffer[8192];
  ArrayOutputStream output(buffer, sizeof(buffer));
  GeneratedCodeInfo info;
  AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
  {
    Printer printer(&output, '$', &info_collector);
    printer.Print("012$foo$4$baz$$bam$$bar$\n", "foo", "3", "bar", "5", "baz",
                  "", "bam", "");
    std::vector<int> path;
    path.push_back(33);
    MockDescriptor descriptor("path", path);
    printer.Annotate("baz", "bam", &descriptor);
  }
  buffer[output.ByteCount()] = '\0';
  EXPECT_STREQ("012345\n", buffer);
  ASSERT_EQ(1, info.annotation_size());
  const GeneratedCodeInfo::Annotation* bazbam = &info.annotation(0);
  ASSERT_EQ(1, bazbam->path_size());
  EXPECT_EQ(33, bazbam->path(0));
  EXPECT_EQ("path", bazbam->source_file());
  EXPECT_EQ(5, bazbam->begin());
  EXPECT_EQ(5, bazbam->end());
}

TEST(Printer, AnnotateDespiteUnrelatedMultipleUses) {
  char buffer[8192];
  ArrayOutputStream output(buffer, sizeof(buffer));
  GeneratedCodeInfo info;
  AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
  {
    Printer printer(&output, '$', &info_collector);
    printer.Print("012$foo$4$foo$$bar$\n", "foo", "3", "bar", "5");
    std::vector<int> path;
    path.push_back(33);
    MockDescriptor descriptor("path", path);
    printer.Annotate("bar", "bar", &descriptor);
  }
  buffer[output.ByteCount()] = '\0';
  EXPECT_STREQ("0123435\n", buffer);
  ASSERT_EQ(1, info.annotation_size());
  const GeneratedCodeInfo::Annotation* bar = &info.annotation(0);
  ASSERT_EQ(1, bar->path_size());
  EXPECT_EQ(33, bar->path(0));
  EXPECT_EQ("path", bar->source_file());
  EXPECT_EQ(6, bar->begin());
  EXPECT_EQ(7, bar->end());
}

TEST(Printer, AnnotateIndent) {
  char buffer[8192];
  ArrayOutputStream output(buffer, sizeof(buffer));
  GeneratedCodeInfo info;
  AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
  {
    Printer printer(&output, '$', &info_collector);
    printer.Print("0\n");
    printer.Indent();
    printer.Print("$foo$", "foo", "4");
    std::vector<int> path;
    path.push_back(44);
    MockDescriptor descriptor("path", path);
    printer.Annotate("foo", &descriptor);
    printer.Print(",\n");
    printer.Print("$bar$", "bar", "9");
    path[0] = 99;
    MockDescriptor descriptor_two("path", path);
    printer.Annotate("bar", &descriptor_two);
    printer.Print("\n${$$D$$}$\n", "{", "", "}", "", "D", "d");
    path[0] = 1313;
    MockDescriptor descriptor_three("path", path);
    printer.Annotate("{", "}", &descriptor_three);
    printer.Outdent();
    printer.Print("\n");
  }
  buffer[output.ByteCount()] = '\0';
  EXPECT_STREQ("0\n  4,\n  9\n  d\n\n", buffer);
  ASSERT_EQ(3, info.annotation_size());
  const GeneratedCodeInfo::Annotation* foo = &info.annotation(0);
  ASSERT_EQ(1, foo->path_size());
  EXPECT_EQ(44, foo->path(0));
  EXPECT_EQ("path", foo->source_file());
  EXPECT_EQ(4, foo->begin());
  EXPECT_EQ(5, foo->end());
  const GeneratedCodeInfo::Annotation* bar = &info.annotation(1);
  ASSERT_EQ(1, bar->path_size());
  EXPECT_EQ(99, bar->path(0));
  EXPECT_EQ("path", bar->source_file());
  EXPECT_EQ(9, bar->begin());
  EXPECT_EQ(10, bar->end());
  const GeneratedCodeInfo::Annotation* braces = &info.annotation(2);
  ASSERT_EQ(1, braces->path_size());
  EXPECT_EQ(1313, braces->path(0));
  EXPECT_EQ("path", braces->source_file());
  EXPECT_EQ(13, braces->begin());
  EXPECT_EQ(14, braces->end());
}

TEST(Printer, AnnotateIndentNewline) {
  char buffer[8192];
  ArrayOutputStream output(buffer, sizeof(buffer));
  GeneratedCodeInfo info;
  AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
  {
    Printer printer(&output, '$', &info_collector);
    printer.Indent();
    printer.Print("$A$$N$$B$C\n", "A", "", "N", "\nz", "B", "");
    std::vector<int> path;
    path.push_back(0);
    MockDescriptor descriptor("path", path);
    printer.Annotate("A", "B", &descriptor);
    printer.Outdent();
    printer.Print("\n");
  }
  buffer[output.ByteCount()] = '\0';
  EXPECT_STREQ("\nz  C\n\n", buffer);
  ASSERT_EQ(1, info.annotation_size());
  const GeneratedCodeInfo::Annotation* ab = &info.annotation(0);
  ASSERT_EQ(1, ab->path_size());
  EXPECT_EQ(0, ab->path(0));
  EXPECT_EQ("path", ab->source_file());
  EXPECT_EQ(0, ab->begin());
  EXPECT_EQ(4, ab->end());
}

TEST(Printer, Indenting) {
  char buffer[8192];

  for (int block_size = 1; block_size < 512; block_size *= 2) {
    ArrayOutputStream output(buffer, sizeof(buffer), block_size);

    {
      Printer printer(&output, '$');
      std::map<string, string> vars;

      vars["newline"] = "\n";

      printer.Print("This is not indented.\n");
      printer.Indent();
      printer.Print("This is indented\nAnd so is this\n");
      printer.Outdent();
      printer.Print("But this is not.");
      printer.Indent();
      printer.Print("  And this is still the same line.\n"
                    "But this is indented.\n");
      printer.PrintRaw("RawBit has indent at start\n");
      printer.PrintRaw("but not after a raw newline\n");
      printer.Print(vars, "Note that a newline in a variable will break "
                    "indenting, as we see$newline$here.\n");
      printer.Indent();
      printer.Print("And this");
      printer.Outdent();
      printer.Outdent();
      printer.Print(" is double-indented\nBack to normal.");

      EXPECT_FALSE(printer.failed());
    }

    buffer[output.ByteCount()] = '\0';

    EXPECT_STREQ(
      "This is not indented.\n"
      "  This is indented\n"
      "  And so is this\n"
      "But this is not.  And this is still the same line.\n"
      "  But this is indented.\n"
      "  RawBit has indent at start\n"
      "but not after a raw newline\n"
      "Note that a newline in a variable will break indenting, as we see\n"
      "here.\n"
      "    And this is double-indented\n"
      "Back to normal.",
      buffer);
  }
}

// Death tests do not work on Windows as of yet.
#ifdef PROTOBUF_HAS_DEATH_TEST
TEST(Printer, Death) {
  char buffer[8192];

  ArrayOutputStream output(buffer, sizeof(buffer));
  Printer printer(&output, '$');

  EXPECT_DEBUG_DEATH(printer.Print("$nosuchvar$"), "Undefined variable");
  EXPECT_DEBUG_DEATH(printer.Print("$unclosed"), "Unclosed variable name");
  EXPECT_DEBUG_DEATH(printer.Outdent(), "without matching Indent");
}

TEST(Printer, AnnotateMultipleUsesDeath) {
  char buffer[8192];
  ArrayOutputStream output(buffer, sizeof(buffer));
  GeneratedCodeInfo info;
  AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
  {
    Printer printer(&output, '$', &info_collector);
    printer.Print("012$foo$4$foo$\n", "foo", "3");
    std::vector<int> path;
    path.push_back(33);
    MockDescriptor descriptor("path", path);
    EXPECT_DEBUG_DEATH(printer.Annotate("foo", "foo", &descriptor), "multiple");
  }
}

TEST(Printer, AnnotateNegativeLengthDeath) {
  char buffer[8192];
  ArrayOutputStream output(buffer, sizeof(buffer));
  GeneratedCodeInfo info;
  AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
  {
    Printer printer(&output, '$', &info_collector);
    printer.Print("012$foo$4$bar$\n", "foo", "3", "bar", "5");
    std::vector<int> path;
    path.push_back(33);
    MockDescriptor descriptor("path", path);
    EXPECT_DEBUG_DEATH(printer.Annotate("bar", "foo", &descriptor), "negative");
  }
}

TEST(Printer, AnnotateUndefinedDeath) {
  char buffer[8192];
  ArrayOutputStream output(buffer, sizeof(buffer));
  GeneratedCodeInfo info;
  AnnotationProtoCollector<GeneratedCodeInfo> info_collector(&info);
  {
    Printer printer(&output, '$', &info_collector);
    printer.Print("012$foo$4$foo$\n", "foo", "3");
    std::vector<int> path;
    path.push_back(33);
    MockDescriptor descriptor("path", path);
    EXPECT_DEBUG_DEATH(printer.Annotate("bar", "bar", &descriptor),
                       "Undefined");
  }
}
#endif  // PROTOBUF_HAS_DEATH_TEST

TEST(Printer, WriteFailurePartial) {
  char buffer[17];

  ArrayOutputStream output(buffer, sizeof(buffer));
  Printer printer(&output, '$');

  // Print 16 bytes to almost fill the buffer (should not fail).
  printer.Print("0123456789abcdef");
  EXPECT_FALSE(printer.failed());

  // Try to print 2 chars. Only one fits.
  printer.Print("<>");
  EXPECT_TRUE(printer.failed());

  // Anything else should fail too.
  printer.Print(" ");
  EXPECT_TRUE(printer.failed());
  printer.Print("blah");
  EXPECT_TRUE(printer.failed());

  // Buffer should contain the first 17 bytes written.
  EXPECT_EQ("0123456789abcdef<", string(buffer, sizeof(buffer)));
}

TEST(Printer, WriteFailureExact) {
  char buffer[16];

  ArrayOutputStream output(buffer, sizeof(buffer));
  Printer printer(&output, '$');

  // Print 16 bytes to fill the buffer exactly (should not fail).
  printer.Print("0123456789abcdef");
  EXPECT_FALSE(printer.failed());

  // Try to print one more byte (should fail).
  printer.Print(" ");
  EXPECT_TRUE(printer.failed());

  // Should not crash
  printer.Print("blah");
  EXPECT_TRUE(printer.failed());

  // Buffer should contain the first 16 bytes written.
  EXPECT_EQ("0123456789abcdef", string(buffer, sizeof(buffer)));
}

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


text:
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// 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 <memory>
#ifndef _SHARED_PTR_H
#include <google/protobuf/stubs/shared_ptr.h>
#endif
#include <vector>

#include <google/protobuf/io/coded_stream.h>

#include <limits.h>

#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>

// This declares an unsigned long long integer literal in a portable way.
// (The original macro is way too big and ruins my formatting.)
#undef ULL
#define ULL(x) GOOGLE_ULONGLONG(x)

namespace google {

namespace protobuf {
namespace io {
namespace {

// ===================================================================
// Data-Driven Test Infrastructure

// TEST_1D and TEST_2D are macros I'd eventually like to see added to
// gTest.  These macros can be used to declare tests which should be
// run multiple times, once for each item in some input array.  TEST_1D
// tests all cases in a single input array.  TEST_2D tests all
// combinations of cases from two arrays.  The arrays must be statically
// defined such that the GOOGLE_ARRAYSIZE() macro works on them.  Example:
//
// int kCases[] = {1, 2, 3, 4}
// TEST_1D(MyFixture, MyTest, kCases) {
//   EXPECT_GT(kCases_case, 0);
// }
//
// This test iterates through the numbers 1, 2, 3, and 4 and tests that
// they are all grater than zero.  In case of failure, the exact case
// which failed will be printed.  The case type must be printable using
// ostream::operator<<.

// TODO(kenton):  gTest now supports "parameterized tests" which would be
//   a better way to accomplish this.  Rewrite when time permits.

#define TEST_1D(FIXTURE, NAME, CASES)                                      \
  class FIXTURE##_##NAME##_DD : public FIXTURE {                           \
   protected:                                                              \
    template <typename CaseType>                                           \
    void DoSingleCase(const CaseType& CASES##_case);                       \
  };                                                                       \
                                                                           \
  TEST_F(FIXTURE##_##NAME##_DD, NAME) {                                    \
    for (int i = 0; i < GOOGLE_ARRAYSIZE(CASES); i++) {                           \
      SCOPED_TRACE(testing::Message()                                      \
        << #CASES " case #" << i << ": " << CASES[i]);                     \
      DoSingleCase(CASES[i]);                                              \
    }                                                                      \
  }                                                                        \
                                                                           \
  template <typename CaseType>                                             \
  void FIXTURE##_##NAME##_DD::DoSingleCase(const CaseType& CASES##_case)

#define TEST_2D(FIXTURE, NAME, CASES1, CASES2)                             \
  class FIXTURE##_##NAME##_DD : public FIXTURE {                           \
   protected:                                                              \
    template <typename CaseType1, typename CaseType2>                      \
    void DoSingleCase(const CaseType1& CASES1##_case,                      \
                      const CaseType2& CASES2##_case);                     \
  };                                                                       \
                                                                           \
  TEST_F(FIXTURE##_##NAME##_DD, NAME) {                                    \
    for (int i = 0; i < GOOGLE_ARRAYSIZE(CASES1); i++) {                          \
      for (int j = 0; j < GOOGLE_ARRAYSIZE(CASES2); j++) {                        \
        SCOPED_TRACE(testing::Message()                                    \
          << #CASES1 " case #" << i << ": " << CASES1[i] << ", "           \
          << #CASES2 " case #" << j << ": " << CASES2[j]);                 \
        DoSingleCase(CASES1[i], CASES2[j]);                                \
      }                                                                    \
    }                                                                      \
  }                                                                        \
                                                                           \
  template <typename CaseType1, typename CaseType2>                        \
  void FIXTURE##_##NAME##_DD::DoSingleCase(const CaseType1& CASES1##_case, \
                                           const CaseType2& CASES2##_case)

// ===================================================================

class CodedStreamTest : public testing::Test {
 protected:
  // Helper method used by tests for bytes warning. See implementation comment
  // for further information.
  static void SetupTotalBytesLimitWarningTest(
      int total_bytes_limit, int warning_threshold,
      std::vector<string>* out_errors, std::vector<string>* out_warnings);

  // Buffer used during most of the tests. This assumes tests run sequentially.
  static const int kBufferSize = 1024 * 64;
  static uint8 buffer_[kBufferSize];
};

uint8 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};

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

struct VarintCase {
  uint8 bytes[10];          // Encoded bytes.
  int size;                 // Encoded size, in bytes.
  uint64 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) |
    (ULL(0x0b) << 28)},

  // 64-bit
  {{0xbe, 0xf7, 0x92, 0x84, 0x1b}, 5,                    // 7256456126
    (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) |
    (ULL(0x1b) << 28)},
  {{0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49}, 8,  // 41256202580718336
    (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
    (ULL(0x43) << 28) | (ULL(0x49) << 35) | (ULL(0x24) << 42) |
    (ULL(0x49) << 49)},
  // 11964378330978735131
  {{0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01}, 10,
    (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) |
    (ULL(0x3b) << 28) | (ULL(0x56) << 35) | (ULL(0x00) << 42) |
    (ULL(0x05) << 49) | (ULL(0x26) << 56) | (ULL(0x01) << 63)},
};

TEST_2D(CodedStreamTest, ReadVarint32, kVarintCases, kBlockSizes) {
  memcpy(buffer_, kVarintCases_case.bytes, kVarintCases_case.size);
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

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

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

TEST_2D(CodedStreamTest, ReadTag, kVarintCases, kBlockSizes) {
  memcpy(buffer_, kVarintCases_case.bytes, kVarintCases_case.size);
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    uint32 expected_value = static_cast<uint32>(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:
    virtual bool Next(const void** data, int* size) {
      *data = NULL;
      *size = 0;
      return count_++ < 2;
    }
    virtual void BackUp(int count)  {
      GOOGLE_LOG(FATAL) << "Tests never call this.";
    }
    virtual bool Skip(int count) {
      GOOGLE_LOG(FATAL) << "Tests never call this.";
      return false;
    }
    virtual int64 ByteCount() const { return 0; }
    int count_;
  } in;
  CodedInputStream input(&in);
  input.ReadTagNoLastTag();
  EXPECT_TRUE(input.ConsumedEntireMessage());
}

TEST_1D(CodedStreamTest, ExpectTag, kVarintCases) {
  // 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 dummy;
    coded_input.ReadRaw(&dummy, 1);
    EXPECT_EQ((uint)'\0', (uint)dummy);

    uint32 expected_value = static_cast<uint32>(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_1D(CodedStreamTest, ExpectTagFromArray, kVarintCases) {
  memcpy(buffer_, kVarintCases_case.bytes, kVarintCases_case.size);

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

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

TEST_2D(CodedStreamTest, ReadVarint64, kVarintCases, kBlockSizes) {
  memcpy(buffer_, kVarintCases_case.bytes, kVarintCases_case.size);
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

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

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

TEST_2D(CodedStreamTest, WriteVarint32, kVarintCases, kBlockSizes) {
  if (kVarintCases_case.value > ULL(0x00000000FFFFFFFF)) {
    // 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>(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_2D(CodedStreamTest, WriteVarint64, kVarintCases, kBlockSizes) {
  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));
}

// This test causes gcc 3.3.5 (and earlier?) to give the cryptic error:
//   "sorry, unimplemented: `method_call_expr' not supported by dump_expr"
#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)

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

TEST_2D(CodedStreamTest, WriteVarint32SignExtended,
        kSignExtendedVarintCases, kBlockSizes) {
  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 value;
    EXPECT_TRUE(coded_input.ReadVarint64(&value));

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

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

#endif

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

struct VarintErrorCase {
  uint8 bytes[12];
  int size;
  bool can_parse;
};

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},

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

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

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

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

TEST_2D(CodedStreamTest, ReadVarint32Error, kVarintErrorCases, kBlockSizes) {
  memcpy(buffer_, kVarintErrorCases_case.bytes, kVarintErrorCases_case.size);
  ArrayInputStream input(buffer_, kVarintErrorCases_case.size,
                         kBlockSizes_case);
  CodedInputStream coded_input(&input);

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

TEST_2D(CodedStreamTest, ReadVarint32Error_LeavesValueInInitializedState,
        kVarintErrorCases, kBlockSizes) {
  memcpy(buffer_, kVarintErrorCases_case.bytes, kVarintErrorCases_case.size);
  ArrayInputStream input(buffer_, kVarintErrorCases_case.size,
                         kBlockSizes_case);
  CodedInputStream coded_input(&input);

  uint32 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_2D(CodedStreamTest, ReadVarint64Error, kVarintErrorCases, kBlockSizes) {
  memcpy(buffer_, kVarintErrorCases_case.bytes, kVarintErrorCases_case.size);
  ArrayInputStream input(buffer_, kVarintErrorCases_case.size,
                         kBlockSizes_case);
  CodedInputStream coded_input(&input);

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

TEST_2D(CodedStreamTest, ReadVarint64Error_LeavesValueInInitializedState,
        kVarintErrorCases, kBlockSizes) {
  memcpy(buffer_, kVarintErrorCases_case.bytes, kVarintErrorCases_case.size);
  ArrayInputStream input(buffer_, kVarintErrorCases_case.size,
                         kBlockSizes_case);
  CodedInputStream coded_input(&input);

  uint64 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 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},
  {ULL(41256202580718336), 8},
  {ULL(11964378330978735131), 10},
};

TEST_1D(CodedStreamTest, VarintSize32, kVarintSizeCases) {
  if (kVarintSizeCases_case.value > 0xffffffffu) {
    // Skip 64-bit values.
    return;
  }

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

TEST_1D(CodedStreamTest, VarintSize64, kVarintSizeCases) {
  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>(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(
                            static_cast<uint64>(0x1ull << i)));
  }
}

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

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

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

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;
}

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

Fixed64Case kFixed64Cases[] = {
  {{0xef, 0xcd, 0xab, 0x90, 0x12, 0x34, 0x56, 0x78}, ULL(0x7856341290abcdef)},
  {{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}, ULL(0x8877665544332211)},
};

TEST_2D(CodedStreamTest, ReadLittleEndian32, kFixed32Cases, kBlockSizes) {
  memcpy(buffer_, kFixed32Cases_case.bytes, sizeof(kFixed32Cases_case.bytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

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

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

TEST_2D(CodedStreamTest, ReadLittleEndian64, kFixed64Cases, kBlockSizes) {
  memcpy(buffer_, kFixed64Cases_case.bytes, sizeof(kFixed64Cases_case.bytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

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

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

TEST_2D(CodedStreamTest, WriteLittleEndian32, kFixed32Cases, kBlockSizes) {
  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), coded_output.ByteCount());
  }

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

TEST_2D(CodedStreamTest, WriteLittleEndian64, kFixed64Cases, kBlockSizes) {
  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), coded_output.ByteCount());
  }

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

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

TEST_1D(CodedStreamTest, ReadLittleEndian32FromArray, kFixed32Cases) {
  memcpy(buffer_, kFixed32Cases_case.bytes, sizeof(kFixed32Cases_case.bytes));

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

TEST_1D(CodedStreamTest, ReadLittleEndian64FromArray, kFixed64Cases) {
  memcpy(buffer_, kFixed64Cases_case.bytes, sizeof(kFixed64Cases_case.bytes));

  uint64 value;
  const uint8* 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.";

TEST_1D(CodedStreamTest, ReadRaw, kBlockSizes) {
  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_1D(CodedStreamTest, WriteRaw, kBlockSizes) {
  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_1D(CodedStreamTest, ReadString, kBlockSizes) {
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    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_1D(CodedStreamTest, ReadStringImpossiblyLarge, kBlockSizes) {
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    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 buffer[8];
  CodedInputStream coded_input(buffer, 8);
  string str;
  EXPECT_FALSE(coded_input.ReadString(&str, 1 << 30));
}

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

TEST_1D(CodedStreamTest, ReadStringReservesMemoryOnTotalLimit, kBlockSizes) {
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

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

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

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

TEST_1D(CodedStreamTest, ReadStringReservesMemoryOnPushedLimit, kBlockSizes) {
  memcpy(buffer_, kRawBytes, sizeof(kRawBytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

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

    string str;
    EXPECT_TRUE(coded_input.ReadString(&str, strlen(kRawBytes)));
    EXPECT_EQ(kRawBytes, str);
    // TODO(liujisi): 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);

    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(liujisi): 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_));

    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(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_));

    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);

    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, 16);

    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, 16);

    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_), sizeof(buffer_));
    EXPECT_EQ(sizeof(buffer_), coded_input.BytesUntilTotalBytesLimit());

    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());
  }
}

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

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

TEST_1D(CodedStreamTest, SkipInput, kBlockSizes) {
  memcpy(buffer_, kSkipTestBytes, sizeof(kSkipTestBytes));
  ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case);

  {
    CodedInputStream coded_input(&input);

    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);
}

TEST_F(CodedStreamTest, GetDirectBufferPointerOutput) {
  ArrayOutputStream output(buffer_, sizeof(buffer_), 8);
  CodedOutputStream coded_output(&output);

  void* ptr;
  int size;

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

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

  // Skip forward in the same buffer then peek again.
  EXPECT_TRUE(coded_output.Skip(3));
  EXPECT_TRUE(coded_output.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_output.Skip(5));
  EXPECT_TRUE(coded_output.GetDirectBufferPointer(&ptr, &size));
  EXPECT_EQ(buffer_ + 8, ptr);
  EXPECT_EQ(8, size);

  // Skip over multiple buffers.
  EXPECT_TRUE(coded_output.Skip(22));
  EXPECT_TRUE(coded_output.GetDirectBufferPointer(&ptr, &size));
  EXPECT_EQ(buffer_ + 30, ptr);
  EXPECT_EQ(2, size);
}

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

TEST_1D(CodedStreamTest, BasicLimit, kBlockSizes) {
  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 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_1D(CodedStreamTest, SmallLimitOnTopOfBigLimit, kBlockSizes) {
  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 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_1D(CodedStreamTest, BigLimitOnTopOfSmallLimit, kBlockSizes) {
  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 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 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, -1);
  EXPECT_EQ(16, coded_input.BytesUntilTotalBytesLimit());

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

  std::vector<string> errors;

  {
    ScopedMemoryLog error_log;
    EXPECT_FALSE(coded_input.ReadString(&str, 1));
    errors = error_log.GetMessages(ERROR);
  }

  ASSERT_EQ(1, errors.size());
  EXPECT_PRED_FORMAT2(testing::IsSubstring,
    "A protocol message was rejected because it was too big", errors[0]);

  coded_input.SetTotalBytesLimit(32, -1);
  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, -1);
  CodedInputStream::Limit limit = coded_input.PushLimit(16);

  // Read 16 bytes.
  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());
}

// This method is used by the tests below.
// It constructs a CodedInputStream with the given limits and tries to read 2KiB
// of data from it. Then it returns the logged errors and warnings in the given
// vectors.
void CodedStreamTest::SetupTotalBytesLimitWarningTest(
    int total_bytes_limit, int warning_threshold,
    std::vector<string>* out_errors, std::vector<string>* out_warnings) {
  ArrayInputStream raw_input(buffer_, sizeof(buffer_), 128);

  ScopedMemoryLog scoped_log;
  {
    CodedInputStream input(&raw_input);
    input.SetTotalBytesLimit(total_bytes_limit, warning_threshold);
    string str;
    EXPECT_TRUE(input.ReadString(&str, 2048));
  }

  *out_errors = scoped_log.GetMessages(ERROR);
  *out_warnings = scoped_log.GetMessages(WARNING);
}

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) {}
  ~ReallyBigInputStream() {}

  // implements ZeroCopyInputStream ----------------------------------
  bool Next(const void** data, int* size) {
    // 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) {
    backup_amount_ = count;
  }

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

  int backup_amount_;

 private:
  char buffer_[1024];
  int64 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;

  std::vector<string> errors;

  {
    ScopedMemoryLog error_log;
    CodedInputStream coded_input(&input);
    string str;
    EXPECT_TRUE(coded_input.ReadString(&str, 512));
    EXPECT_TRUE(coded_input.ReadString(&str, 1024));
    errors = error_log.GetMessages(ERROR);
  }

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

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

mime_type: text/plain


text:
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// A locale-independent version of strtod(), used to parse floating
// point default values in .proto files, where the decimal separator
// is always a dot.

#ifndef GOOGLE_PROTOBUF_IO_STRTOD_H__
#define GOOGLE_PROTOBUF_IO_STRTOD_H__

namespace google {
namespace protobuf {
namespace io {

// A locale-independent version of the standard strtod(), which always
// uses a dot as the decimal separator.
double NoLocaleStrtod(const char* str, char** endptr);

// Casts a double value to a float value. If the value is outside of the
// representable range of float, it will be converted to positive or negative
// infinity.
float SafeDoubleToFloat(double value);

}  // namespace io
}  // namespace protobuf

}  // namespace google
#endif  // GOOGLE_PROTOBUF_IO_STRTOD_H__