| // 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 <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include "absl/types/optional.h" |
| #include "google/protobuf/wire_format_lite.h" |
| |
| namespace google { |
| namespace protobuf { |
| namespace internal { |
| |
| namespace { |
| |
| using ::testing::Eq; |
| using ::testing::Not; |
| using ::testing::Optional; |
| |
| // The fast parser's dispatch table Xors two bytes of incoming data with |
| // the data in TcFieldData, so we reproduce that here: |
| TcFieldData Xor2SerializedBytes(TcFieldData tfd, const char* ptr) { |
| uint64_t twobytes = 0xFF & ptr[0]; |
| twobytes |= (0xFF & ptr[1]) << 8; |
| tfd.data ^= twobytes; |
| return tfd; |
| } |
| |
| absl::optional<const char*> fallback_ptr_received; |
| absl::optional<uint64_t> fallback_hasbits_received; |
| absl::optional<uint64_t> fallback_tag_received; |
| const char* FastParserGaveUp(::google::protobuf::MessageLite*, const char* ptr, |
| ::google::protobuf::internal::ParseContext*, |
| ::google::protobuf::internal::TcFieldData data, |
| const ::google::protobuf::internal::TcParseTableBase*, |
| uint64_t hasbits) { |
| fallback_ptr_received = ptr; |
| fallback_hasbits_received = hasbits; |
| fallback_tag_received = data.tag(); |
| return nullptr; |
| } |
| |
| // To test that we aren't storing too much data, we set up a fake message area |
| // and fill all its bytes with kDND. |
| constexpr char kDND = 0x5A; // "Do Not Disturb" |
| |
| // To retrieve data and see if it matches what we expect, we have this routine |
| // which simultaneously reads the data we want, and sets it back to what it was |
| // before the test, that is, to kDND. This makes it easier to test at the end |
| // that all the original data is undisturbed. |
| template <typename T> |
| T ReadAndReset(char* p) { |
| T result; |
| memcpy(&result, p, sizeof(result)); |
| memset(p, kDND, sizeof(result)); |
| return result; |
| } |
| |
| TEST(FastVarints, NameHere) { |
| constexpr uint8_t kHasBitsOffset = 4; |
| constexpr uint8_t kHasBitIndex = 0; |
| constexpr uint8_t kFieldOffset = 24; |
| |
| // clang-format on |
| const TcParseTable<0, 1, 0, 0, 2> parse_table = { |
| { |
| kHasBitsOffset, // |
| 0, 0, 0, // no _extensions_ |
| 1, 0, // max_field_number, fast_idx_mask |
| offsetof(decltype(parse_table), field_lookup_table), |
| 0xFFFFFFFF - 1, // skipmap |
| offsetof(decltype(parse_table), field_entries), |
| 1, // num_field_entries |
| 0, // num_aux_entries |
| offsetof(decltype(parse_table), field_names), // no aux_entries |
| nullptr, // default instance |
| FastParserGaveUp, // fallback |
| }, |
| // Fast Table: |
| {{ |
| // optional int32 field = 1; |
| {TcParser::SingularVarintNoZag1<::uint32_t, kFieldOffset, |
| kHasBitIndex>(), |
| {/* coded_tag= */ 8, kHasBitIndex, /* aux_idx= */ 0, kFieldOffset}}, |
| }}, |
| // Field Lookup Table: |
| {{65535, 65535}}, |
| // Field Entries: |
| {{ |
| // This is set to kFkNone to force MiniParse to call the fallback |
| {kFieldOffset, kHasBitsOffset + 0, 0, (field_layout::kFkNone)}, |
| }}, |
| // no aux_entries |
| {{}}, |
| }; |
| // clang-format on |
| uint8_t serialize_buffer[64]; |
| |
| for (int size : {8, 32, 64, -8, -32, -64}) { |
| SCOPED_TRACE(size); |
| auto next_i = [](uint64_t i) { |
| // if i + 1 is a power of two, return that. |
| // (This will also match when i == -1, but for this loop we know that will |
| // not happen.) |
| if ((i & (i + 1)) == 0) return i + 1; |
| // otherwise, i is already a power of two, so advance to one less than the |
| // next power of two. |
| return i + (i - 1); |
| }; |
| for (uint64_t i = 0; i + 1 != 0; i = next_i(i)) { |
| SCOPED_TRACE(i); |
| enum OverlongKind { kNotOverlong, kOverlong, kOverlongWithDroppedBits }; |
| for (OverlongKind overlong : |
| {kNotOverlong, kOverlong, kOverlongWithDroppedBits}) { |
| SCOPED_TRACE(overlong); |
| alignas(16) char fake_msg[64] = { |
| kDND, kDND, kDND, kDND, kDND, kDND, kDND, kDND, // |
| kDND, kDND, kDND, kDND, kDND, kDND, kDND, kDND, // |
| kDND, kDND, kDND, kDND, kDND, kDND, kDND, kDND, // |
| kDND, kDND, kDND, kDND, kDND, kDND, kDND, kDND, // |
| kDND, kDND, kDND, kDND, kDND, kDND, kDND, kDND, // |
| kDND, kDND, kDND, kDND, kDND, kDND, kDND, kDND, // |
| kDND, kDND, kDND, kDND, kDND, kDND, kDND, kDND, // |
| kDND, kDND, kDND, kDND, kDND, kDND, kDND, kDND, // |
| }; |
| memset(&fake_msg[kHasBitsOffset], 0, sizeof(uint32_t)); |
| |
| auto serialize_ptr = WireFormatLite::WriteUInt64ToArray( |
| /* field_number= */ 1, i, serialize_buffer); |
| |
| if (overlong == kOverlong || overlong == kOverlongWithDroppedBits) { |
| // 1 for the tag plus 10 for the value |
| while (serialize_ptr - serialize_buffer < 11) { |
| serialize_ptr[-1] |= 0x80; |
| *serialize_ptr++ = 0; |
| } |
| if (overlong == kOverlongWithDroppedBits) { |
| // For this one we add some unused bits to the last byte. |
| // They should be dropped. Bits 1-6 are dropped. Bit 0 is used and |
| // bit 7 is checked for continuation. |
| serialize_ptr[-1] |= 0b0111'1110; |
| } |
| } |
| |
| absl::string_view serialized{ |
| reinterpret_cast<char*>(&serialize_buffer[0]), |
| static_cast<size_t>(serialize_ptr - serialize_buffer)}; |
| |
| const char* ptr = nullptr; |
| const char* end_ptr = nullptr; |
| ParseContext ctx(io::CodedInputStream::GetDefaultRecursionLimit(), |
| /* aliasing= */ false, &ptr, serialized); |
| #if 0 // FOR_DEBUGGING |
| ABSL_LOG(ERROR) << "size=" << size << " i=" << i << " ptr points to " // |
| << +ptr[0] << "," << +ptr[1] << "," // |
| << +ptr[2] << "," << +ptr[3] << "," // |
| << +ptr[4] << "," << +ptr[5] << "," // |
| << +ptr[6] << "," << +ptr[7] << "," // |
| << +ptr[8] << "," << +ptr[9] << "," << +ptr[10] << "\n"; |
| #endif |
| TailCallParseFunc fn = nullptr; |
| switch (size) { |
| case 8: |
| fn = &TcParser::FastV8S1; |
| break; |
| case -8: |
| fn = &TcParser::FastTV8S1<kFieldOffset, kHasBitIndex>; |
| break; |
| case 32: |
| fn = &TcParser::FastV32S1; |
| break; |
| case -32: |
| fn = &TcParser::FastTV32S1<uint32_t, kFieldOffset, kHasBitIndex>; |
| break; |
| case 64: |
| fn = &TcParser::FastV64S1; |
| break; |
| case -64: |
| fn = &TcParser::FastTV64S1<uint64_t, kFieldOffset, kHasBitIndex>; |
| break; |
| } |
| fallback_ptr_received = absl::nullopt; |
| fallback_hasbits_received = absl::nullopt; |
| fallback_tag_received = absl::nullopt; |
| end_ptr = fn(reinterpret_cast<MessageLite*>(fake_msg), ptr, &ctx, |
| Xor2SerializedBytes(parse_table.fast_entries[0].bits, ptr), |
| &parse_table.header, /*hasbits=*/0); |
| switch (size) { |
| case -8: |
| case 8: { |
| if (end_ptr == nullptr) { |
| // If end_ptr is nullptr, that means the FastParser gave up and |
| // tried to pass control to MiniParse.... which is expected |
| // anytime we encounter something other than 0 or 1 encodings. |
| // (Since FastV8S1 is only used for `bool` fields.) |
| if (overlong == kNotOverlong) { |
| EXPECT_NE(i, true); |
| EXPECT_NE(i, false); |
| } |
| EXPECT_THAT(fallback_hasbits_received, Optional(0)); |
| // Like the mini-parser functions, and unlike the fast-parser |
| // functions, the fallback receives a ptr already incremented past |
| // the tag, and receives the actual tag in the `data` parameter. |
| EXPECT_THAT(fallback_ptr_received, Optional(ptr + 1)); |
| EXPECT_THAT(fallback_tag_received, Optional(0x7F & *ptr)); |
| continue; |
| } |
| ASSERT_EQ(end_ptr - ptr, serialized.size()); |
| |
| auto actual_field = ReadAndReset<uint8_t>(&fake_msg[kFieldOffset]); |
| EXPECT_EQ(actual_field, static_cast<decltype(actual_field)>(i)) // |
| << " hex: " << absl::StrCat(absl::Hex(actual_field)); |
| }; break; |
| case -32: |
| case 32: { |
| ASSERT_TRUE(end_ptr); |
| ASSERT_EQ(end_ptr - ptr, serialized.size()); |
| |
| auto actual_field = ReadAndReset<uint32_t>(&fake_msg[kFieldOffset]); |
| EXPECT_EQ(actual_field, static_cast<decltype(actual_field)>(i)) // |
| << " hex: " << absl::StrCat(absl::Hex(actual_field)); |
| }; break; |
| case -64: |
| case 64: { |
| ASSERT_EQ(end_ptr - ptr, serialized.size()); |
| |
| auto actual_field = ReadAndReset<uint64_t>(&fake_msg[kFieldOffset]); |
| EXPECT_EQ(actual_field, static_cast<decltype(actual_field)>(i)) // |
| << " hex: " << absl::StrCat(absl::Hex(actual_field)); |
| }; break; |
| } |
| EXPECT_TRUE(!fallback_ptr_received); |
| EXPECT_TRUE(!fallback_hasbits_received); |
| EXPECT_TRUE(!fallback_tag_received); |
| auto hasbits = ReadAndReset<uint32_t>(&fake_msg[kHasBitsOffset]); |
| EXPECT_EQ(hasbits, 1 << kHasBitIndex); |
| |
| int offset = 0; |
| for (char ch : fake_msg) { |
| EXPECT_EQ(ch, kDND) << " corruption of message at offset " << offset; |
| ++offset; |
| } |
| } |
| } |
| } |
| } |
| |
| MATCHER_P3(IsEntryForFieldNum, table, field_num, field_numbers_table, |
| absl::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 absl::string_view 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 absl::string_view 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}) { |
| ABSL_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)); |
| |
| absl::string_view name = FieldName(table, entry); |
| EXPECT_EQ(name.length(), field_num); |
| while (name[0] == '0') name.remove_prefix(1); // strip leading zeores |
| EXPECT_EQ(name, absl::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); |
| absl::string_view 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 |