// 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 #include #include #include #include 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(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 static const TcParseTableBase::FieldEntry* FindFieldEntry( const TcParseTable& table, uint32_t tag) { return TcParser::FindFieldEntry(&table.header, tag); } // Calls the private `FieldName` function. template static StringPiece FieldName( const TcParseTable& table, const TcParseTableBase::FieldEntry* entry) { return TcParser::FieldName(&table.header, entry); } // Calls the private `MessageName` function. template static StringPiece MessageName( const TcParseTable& 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