blob: 4310803a63d6c940d679f938bc1241a0ad233bfa [file] [log] [blame]
// 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.
#include <cstddef>
#include <google/protobuf/generated_message_tctable_impl.h>
#include <google/protobuf/wire_format_lite.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace google {
namespace protobuf {
namespace internal {
namespace {
using ::testing::Eq;
using ::testing::Not;
MATCHER_P3(IsEntryForFieldNum, table, field_num, field_numbers_table,
StrCat(negation ? "isn't " : "",
"the field entry for field number ", field_num)) {
if (arg == nullptr) {
*result_listener << "which is nullptr";
return false;
}
// Use the entry's index to compare field numbers.
size_t index = static_cast<const TcParseTableBase::FieldEntry*>(arg) -
&table->field_entries[0];
uint32_t actual_field_num = field_numbers_table[index];
if (actual_field_num != field_num) {
*result_listener << "which is the entry for " << actual_field_num;
return false;
}
return true;
}
TEST(IsEntryForFieldNumTest, Matcher) {
// clang-format off
TcParseTable<0, 3, 0, 0, 2> table = {
// header:
{
0, 0, 0, 0, // has_bits_offset, extensions
0, // max_field_number
0, // fast_idx_mask,
offsetof(decltype(table), field_lookup_table),
0xFFFFFFFF - 7, // 7 = fields 1, 2, and 3.
offsetof(decltype(table), field_names),
0, // num_field_entries
0, 0, // num_aux_entries, aux_offset,
nullptr, // default instance
nullptr, // fallback function
}};
// clang-format on
int table_field_numbers[] = {1, 2, 3};
table.field_lookup_table = {65535, 65535};
auto& entries = table.field_entries;
EXPECT_THAT(&entries[0], IsEntryForFieldNum(&table, 1, table_field_numbers));
EXPECT_THAT(&entries[2], IsEntryForFieldNum(&table, 3, table_field_numbers));
EXPECT_THAT(&entries[1],
Not(IsEntryForFieldNum(&table, 3, table_field_numbers)));
EXPECT_THAT(nullptr, Not(IsEntryForFieldNum(&table, 1, table_field_numbers)));
}
} // namespace
class FindFieldEntryTest : public ::testing::Test {
public:
// Calls the private `FindFieldEntry` function.
template <size_t kFastTableSizeLog2, size_t kNumEntries, size_t kNumFieldAux,
size_t kNameTableSize, size_t kFieldLookupTableSize>
static const TcParseTableBase::FieldEntry* FindFieldEntry(
const TcParseTable<kFastTableSizeLog2, kNumEntries, kNumFieldAux,
kNameTableSize, kFieldLookupTableSize>& table,
uint32_t tag) {
return TcParser::FindFieldEntry(&table.header, tag);
}
// Calls the private `FieldName` function.
template <size_t kFastTableSizeLog2, size_t kNumEntries, size_t kNumFieldAux,
size_t kNameTableSize, size_t kFieldLookupTableSize>
static StringPiece FieldName(
const TcParseTable<kFastTableSizeLog2, kNumEntries, kNumFieldAux,
kNameTableSize, kFieldLookupTableSize>& table,
const TcParseTableBase::FieldEntry* entry) {
return TcParser::FieldName(&table.header, entry);
}
// Calls the private `MessageName` function.
template <size_t kFastTableSizeLog2, size_t kNumEntries, size_t kNumFieldAux,
size_t kNameTableSize, size_t kFieldLookupTableSize>
static StringPiece MessageName(
const TcParseTable<kFastTableSizeLog2, kNumEntries, kNumFieldAux,
kNameTableSize, kFieldLookupTableSize>& table) {
return TcParser::MessageName(&table.header);
}
// Returns the number of fields scanned during a small scan.
static constexpr int small_scan_size() { return TcParser::kMtSmallScanSize; }
};
TEST_F(FindFieldEntryTest, SequentialFieldRange) {
// Look up fields that are within the range of `lookup_table_offset`.
// clang-format off
TcParseTable<0, 5, 0, 0, 8> table = {
// header:
{
0, 0, 0, 0, // has_bits_offset, extensions
111, // max_field_number
0, // fast_idx_mask,
offsetof(decltype(table), field_lookup_table),
0xFFFFFFFF - (1 << 1) - (1 << 2) // fields 2, 3
- (1 << 3) - (1 << 4), // fields 4, 5
offsetof(decltype(table), field_entries),
5, // num_field_entries
0, 0, // num_aux_entries, aux_offset,
nullptr, // default instance
{}, // fallback function
},
{}, // fast_entries
// field_lookup_table for 2, 3, 4, 5, 111:
{{
111, 0, // field 111
1, // 1 skip entry
0xFFFE, 4, // 1 field, entry 4.
65535, 65535, // end of table
}},
};
// clang-format on
int table_field_numbers[] = {2, 3, 4, 5, 111};
for (int i : table_field_numbers) {
EXPECT_THAT(FindFieldEntry(table, i),
IsEntryForFieldNum(&table, i, table_field_numbers));
}
for (int i : {0, 1, 6, 7, 110, 112, 500000000}) {
GOOGLE_LOG(WARNING) << "Field " << i;
EXPECT_THAT(FindFieldEntry(table, i), Eq(nullptr));
}
}
TEST_F(FindFieldEntryTest, SmallScanRange) {
// Look up fields past `lookup_table_offset`, but before binary search.
ASSERT_THAT(small_scan_size(), Eq(4)) << "test needs to be updated";
// clang-format off
TcParseTable<0, 6, 0, 0, 8> table = {
// header:
{
0, 0, 0, 0, // has_bits_offset, extensions
111, // max_field_number
0, // fast_idx_mask,
offsetof(decltype(table), field_lookup_table),
0xFFFFFFFF - (1<<0) - (1<<2) - (1<<3) - (1<<4) - (1<<6), // 1,3-5,7
offsetof(decltype(table), field_entries),
6, // num_field_entries
0, 0, // num_aux_entries, aux_offset,
nullptr, // default instance
{}, // fallback function
},
{}, // fast_entries
// field_lookup_table for 1, 3, 4, 5, 7, 111:
{{
111, 0, // field 111
1, // 1 skip entry
0xFFFE, 5, // 1 field, entry 5
65535, 65535 // end of table
}},
};
// clang-format on
int table_field_numbers[] = {// Sequential entries:
1,
// Small scan range:
3, 4, 5, 7,
// Binary search range:
111};
for (int i : table_field_numbers) {
EXPECT_THAT(FindFieldEntry(table, i),
IsEntryForFieldNum(&table, i, table_field_numbers));
}
for (int i : {0, 2, 6, 8, 9, 110, 112, 500000000}) {
EXPECT_THAT(FindFieldEntry(table, i), Eq(nullptr));
}
}
TEST_F(FindFieldEntryTest, BinarySearchRange) {
// Fields after the sequential and small-scan ranges are looked up using
// binary search.
ASSERT_THAT(small_scan_size(), Eq(4)) << "test needs to be updated";
// clang-format off
TcParseTable<0, 10, 0, 0, 8> table = {
// header:
{
0, 0, 0, 0, // has_bits_offset, extensions
70, // max_field_number
0, // fast_idx_mask,
offsetof(decltype(table), field_lookup_table),
0xFFFFFFFF - (1<<0) - (1<<2) - (1<<3) - (1<<4) // 1, 3, 4, 5, 6
- (1<<5) - (1<<7) - (1<<8) - (1<<10) // 8, 9, 11, 12
- (1<<11),
offsetof(decltype(table), field_entries),
10, // num_field_entries
0, 0, // num_aux_entries, aux_offset,
nullptr, // default instance
{}, // fallback function
},
{}, // fast_entries
// field_lookup_table for 1, 3, 4, 5, 6, 8, 9, 11, 12, 70
{{
70, 0, // field 70
1, // 1 skip entry
0xFFFE, 9, // 1 field, entry 9
65535, 65535 // end of table
}},
};
int table_field_numbers[] = {
// Sequential entries:
1,
// Small scan range:
3, 4, 5, 6,
// Binary search range:
8, 9, 11, 12, 70
};
// clang-format on
for (int i : table_field_numbers) {
EXPECT_THAT(FindFieldEntry(table, i),
IsEntryForFieldNum(&table, i, table_field_numbers));
}
for (int i : {0, 2, 7, 10, 13, 69, 71, 112, 500000000}) {
EXPECT_THAT(FindFieldEntry(table, i), Eq(nullptr));
}
}
TEST_F(FindFieldEntryTest, OutOfRange) {
// Look up tags that are larger than the maximum in the message.
// clang-format off
TcParseTable<0, 3, 0, 15, 2> table = {
// header:
{
0, 0, 0, 0, // has_bits_offset, extensions
3, // max_field_number
0, // fast_idx_mask,
offsetof(decltype(table), field_lookup_table),
0xFFFFFFFF - (1<<0) - (1<<1) - (1<<2), // fields 1, 2, 3
offsetof(decltype(table), field_entries),
3, // num_field_entries
0, // num_aux_entries
offsetof(decltype(table), field_names), // no aux_entries
nullptr, // default instance
{}, // fallback function
},
{}, // fast_entries
{{// field lookup table
65535, 65535 // end of table
}},
{}, // "mini" table
// auxiliary entries (none in this test)
{{ // name lengths
"\0\1\2\3\0\0\0\0"
// names
"1"
"02"
"003"}},
};
// clang-format on
int table_field_numbers[] = {1, 2, 3};
for (int field_num : table_field_numbers) {
auto* entry = FindFieldEntry(table, field_num);
EXPECT_THAT(entry,
IsEntryForFieldNum(&table, field_num, table_field_numbers));
StringPiece name = FieldName(table, entry);
EXPECT_EQ(name.length(), field_num);
while (name[0] == '0') name.remove_prefix(1); // strip leading zeores
EXPECT_EQ(name, StrCat(field_num));
}
for (int field_num : {0, 4, 112, 500000000}) {
EXPECT_THAT(FindFieldEntry(table, field_num), Eq(nullptr));
}
}
TEST_F(FindFieldEntryTest, EmptyMessage) {
// Ensure that tables with no fields are handled correctly.
using TableType = TcParseTable<0, 0, 0, 20, 2>;
// clang-format off
TableType table = {
// header:
{
0, 0, 0, 0, // has_bits_offset, extensions
0, // max_field_number
0, // fast_idx_mask,
offsetof(decltype(table), field_lookup_table),
0xFFFFFFFF, // no fields
offsetof(decltype(table), field_names), // no field_entries
0, // num_field_entries
0, // num_aux_entries
offsetof(TableType, field_names),
nullptr, // default instance
nullptr, // fallback function
},
{}, // fast_entries
{{// empty field lookup table
65535, 65535
}},
{{
"\13\0\0\0\0\0\0\0"
"MessageName"
}},
};
// clang-format on
for (int i : {0, 4, 112, 500000000}) {
EXPECT_THAT(FindFieldEntry(table, i), Eq(nullptr));
}
EXPECT_THAT(MessageName(table), Eq("MessageName"));
}
// Make a monster with lots of field numbers
int32_t test_all_types_table_field_numbers[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, //
11, 12, 13, 14, 15, 18, 19, 21, 22, 24, //
25, 27, 31, 32, 33, 34, 35, 36, 37, 38, //
39, 40, 41, 42, 43, 44, 45, 48, 49, 51, //
52, 54, 55, 56, 57, 58, 59, 60, 61, 62, //
63, 64, 65, 66, 67, 68, 69, 70, 71, 72, //
73, 74, 75, 76, 77, 78, 79, 80, 81, 82, //
83, 84, 85, 86, 87, 88, 89, 90, 91, 92, //
93, 94, 95, 96, 97, 98, 99, 100, 101, 102, //
111, 112, 113, 114, 115, 116, 117, 118, 119, 201, //
241, 242, 243, 244, 245, 246, 247, 248, 249, 250, //
251, 252, 253, 254, 255, 321, 322, 401, 402, 403, //
404, 405, 406, 407, 408, 409, 410, 411, 412, 413, //
414, 415, 416, 417};
// clang-format off
const TcParseTable<5, 134, 5, 2176, 55> test_all_types_table = {
// header:
{
0, 0, 0, 0, // has_bits_offset, extensions
418, 248, // max_field_number, fast_idx_mask
offsetof(decltype(test_all_types_table), field_lookup_table),
977895424, // skipmap for fields 1-15,18-19,21-22,24-25,27,31-32
offsetof(decltype(test_all_types_table), field_entries),
135, // num_field_entries
5, // num_aux_entries
offsetof(decltype(test_all_types_table), aux_entries),
nullptr, // default instance
nullptr, // fallback function
},
{{
// tail-call table
}},
{{ // field lookup table
//
// fields 33-417, over 25 skipmap / offset pairs
33, 0, 25,
24576, 24, 18, 38, 0, 52, 0, 68, 16320, 84,
65408, 92, 65535, 99, 65535, 99, 65535, 99, 65535, 99,
65279, 99, 65535, 100, 65535, 100, 32768, 100, 65535, 115,
65535, 115, 65535, 115, 65535, 115, 65532, 115, 65535, 117,
65535, 117, 65535, 117, 65535, 117, 0, 117, 65532, 133,
// end of table
65535, 65535
}},
{{
// "mini" table
}},
{{ // auxiliary entries (not used in this test)
{-1, 4},
{-1, 4},
{-1, 4},
{-1, 4},
{-1, 4},
}}, {{ // name lengths
"\1" // message name
"\16\16\17\17\17\17\20\20\21\21\16\17\15\17\16\27\30\24\25\25"
"\15\21\16\16\17\17\17\17\20\20\21\21\16\17\15\17\16\27\30\24"
"\25\25\15\17\17\21\21\21\21\23\23\25\25\17\20\15\21\20\31\32"
"\26\27\14\14\15\15\15\15\16\16\17\17\14\15\13\22\16\16\17\17"
"\17\17\20\20\21\21\16\17\15\24\14\24\14\13\12\14\13\14\12\4"
"\15\15\16\16\16\16\17\17\20\20\15\16\14\16\15\25\25\12\13\14"
"\15\13\15\12\12\13\14\14\14\16\16\15\15\16\0"
// names
"M"
"optional_int32"
"optional_int64"
"optional_uint32"
"optional_uint64"
"optional_sint32"
"optional_sint64"
"optional_fixed32"
"optional_fixed64"
"optional_sfixed32"
"optional_sfixed64"
"optional_float"
"optional_double"
"optional_bool"
"optional_string"
"optional_bytes"
"optional_nested_message"
"optional_foreign_message"
"optional_nested_enum"
"optional_foreign_enum"
"optional_string_piece"
"optional_cord"
"recursive_message"
"repeated_int32"
"repeated_int64"
"repeated_uint32"
"repeated_uint64"
"repeated_sint32"
"repeated_sint64"
"repeated_fixed32"
"repeated_fixed64"
"repeated_sfixed32"
"repeated_sfixed64"
"repeated_float"
"repeated_double"
"repeated_bool"
"repeated_string"
"repeated_bytes"
"repeated_nested_message"
"repeated_foreign_message"
"repeated_nested_enum"
"repeated_foreign_enum"
"repeated_string_piece"
"repeated_cord"
"map_int32_int32"
"map_int64_int64"
"map_uint32_uint32"
"map_uint64_uint64"
"map_sint32_sint32"
"map_sint64_sint64"
"map_fixed32_fixed32"
"map_fixed64_fixed64"
"map_sfixed32_sfixed32"
"map_sfixed64_sfixed64"
"map_int32_float"
"map_int32_double"
"map_bool_bool"
"map_string_string"
"map_string_bytes"
"map_string_nested_message"
"map_string_foreign_message"
"map_string_nested_enum"
"map_string_foreign_enum"
"packed_int32"
"packed_int64"
"packed_uint32"
"packed_uint64"
"packed_sint32"
"packed_sint64"
"packed_fixed32"
"packed_fixed64"
"packed_sfixed32"
"packed_sfixed64"
"packed_float"
"packed_double"
"packed_bool"
"packed_nested_enum"
"unpacked_int32"
"unpacked_int64"
"unpacked_uint32"
"unpacked_uint64"
"unpacked_sint32"
"unpacked_sint64"
"unpacked_fixed32"
"unpacked_fixed64"
"unpacked_sfixed32"
"unpacked_sfixed64"
"unpacked_float"
"unpacked_double"
"unpacked_bool"
"unpacked_nested_enum"
"oneof_uint32"
"oneof_nested_message"
"oneof_string"
"oneof_bytes"
"oneof_bool"
"oneof_uint64"
"oneof_float"
"oneof_double"
"oneof_enum"
"data"
"default_int32"
"default_int64"
"default_uint32"
"default_uint64"
"default_sint32"
"default_sint64"
"default_fixed32"
"default_fixed64"
"default_sfixed32"
"default_sfixed64"
"default_float"
"default_double"
"default_bool"
"default_string"
"default_bytes"
"optional_lazy_message"
"repeated_lazy_message"
"fieldname1"
"field_name2"
"_field_name3"
"field__name4_"
"field0name5"
"field_0_name6"
"fieldName7"
"FieldName8"
"field_Name9"
"Field_Name10"
"FIELD_NAME11"
"FIELD_name12"
"__field_name13"
"__Field_name14"
"field__name15"
"field__Name16"
"field_name17__"
}},
};
// clang-format on
TEST_F(FindFieldEntryTest, BigMessage) {
EXPECT_THAT(MessageName(test_all_types_table), Eq("M"));
for (int field_num :
{1, 12, 31, 42, 57, 68, 79, 90, 101, 119, 249, 402, 412}) {
auto* entry = FindFieldEntry(test_all_types_table, field_num);
StringPiece name = FieldName(test_all_types_table, entry);
switch (field_num) {
case 1:
EXPECT_THAT(name, Eq("optional_int32"));
break;
case 12:
EXPECT_THAT(name, Eq("optional_double"));
break;
case 31:
EXPECT_THAT(name, Eq("repeated_int32"));
break;
case 42:
EXPECT_THAT(name, Eq("repeated_double"));
break;
case 57:
EXPECT_THAT(name, Eq("map_int64_int64"));
break;
case 68:
EXPECT_THAT(name, Eq("map_bool_bool"));
break;
case 79:
EXPECT_THAT(name, Eq("packed_sint32"));
break;
case 90:
EXPECT_THAT(name, Eq("unpacked_int64"));
break;
case 101:
EXPECT_THAT(name, Eq("unpacked_bool"));
break;
case 119:
EXPECT_THAT(name, Eq("oneof_enum"));
break;
case 249:
EXPECT_THAT(name, Eq("default_sfixed32"));
break;
case 402:
EXPECT_THAT(name, Eq("field_name2"));
break;
case 412:
EXPECT_THAT(name, Eq("FIELD_name12"));
break;
}
}
}
} // namespace internal
} // namespace protobuf
} // namespace google