You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1104 lines
37 KiB
1104 lines
37 KiB
5 years ago
|
// Protocol Buffers - Google's data interchange format
|
||
|
// Copyright 2008 Google Inc. All rights reserved.
|
||
|
// http://code.google.com/p/protobuf/
|
||
|
//
|
||
|
// 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 <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<<.
|
||
|
|
||
|
// 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:
|
||
|
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_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, 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);
|
||
|
|
||
|
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) {
|
||
|
scoped_array<uint8> buffer(new uint8[8]);
|
||
|
CodedInputStream coded_input(buffer.get(), 8);
|
||
|
string str;
|
||
|
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());
|
||
|
}
|
||
|
|
||
|
// -------------------------------------------------------------------
|
||
|
// 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);
|
||
|
|
||
|
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
|