aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/google/protobuf/io/coded_stream_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/google/protobuf/io/coded_stream_unittest.cc')
-rw-r--r--src/google/protobuf/io/coded_stream_unittest.cc929
1 files changed, 929 insertions, 0 deletions
diff --git a/src/google/protobuf/io/coded_stream_unittest.cc b/src/google/protobuf/io/coded_stream_unittest.cc
new file mode 100644
index 00000000..c1a88349
--- /dev/null
+++ b/src/google/protobuf/io/coded_stream_unittest.cc
@@ -0,0 +1,929 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc.
+// http://code.google.com/p/protobuf/
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// 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 <vector>
+
+#include <google/protobuf/io/coded_stream.h>
+
+#include <limits.h>
+
+#include <google/protobuf/stubs/common.h>
+#include <google/protobuf/testing/googletest.h>
+#include <gtest/gtest.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/stubs/strutil.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<<.
+
+#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:
+ 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());
+}
+
+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_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);
+
+ EXPECT_TRUE(coded_output.WriteVarint32(
+ static_cast<uint32>(kVarintCases_case.value)));
+
+ 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);
+
+ EXPECT_TRUE(coded_output.WriteVarint64(kVarintCases_case.value));
+
+ 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);
+
+ EXPECT_TRUE(coded_output.WriteVarint32SignExtended(
+ kSignExtendedVarintCases_case));
+
+ 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, 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));
+}
+
+// -------------------------------------------------------------------
+// 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));
+}
+
+// -------------------------------------------------------------------
+// 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" << hex << c.value << dec;
+}
+
+inline std::ostream& operator<<(std::ostream& os, const Fixed64Case& c) {
+ return os << "0x" << hex << c.value << 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);
+
+ EXPECT_TRUE(coded_output.WriteLittleEndian32(kFixed32Cases_case.value));
+
+ 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);
+
+ EXPECT_TRUE(coded_output.WriteLittleEndian64(kFixed64Cases_case.value));
+
+ EXPECT_EQ(sizeof(uint64), coded_output.ByteCount());
+ }
+
+ EXPECT_EQ(sizeof(uint64), output.ByteCount());
+ EXPECT_EQ(0, memcmp(buffer_, kFixed64Cases_case.bytes, sizeof(uint64)));
+}
+
+// -------------------------------------------------------------------
+// Raw reads and writes
+
+const char kRawBytes[] = "Some bytes which will be writted 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);
+
+ EXPECT_TRUE(coded_output.WriteRaw(kRawBytes, sizeof(kRawBytes)));
+
+ 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));
+ }
+}
+
+
+// -------------------------------------------------------------------
+// Skip
+
+const char kSkipTestBytes[] =
+ "<Before skipping><To be skipped><After skipping>";
+const char kSkipOutputTestBytes[] =
+ "-----------------<To be skipped>----------------";
+
+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());
+}
+
+// -------------------------------------------------------------------
+// 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);
+
+ string str;
+ EXPECT_TRUE(coded_input.ReadString(&str, 16));
+
+ 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_TRUE(coded_input.ReadString(&str, 16));
+}
+
+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.ReadTag());
+ 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.ReadTag());
+ 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) {}
+ ~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;
+
+ 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