Fix upb_MiniTable_Equals, add test.

PiperOrigin-RevId: 567806459
pull/14192/head
Protobuf Team Bot 1 year ago committed by Copybara-Service
parent cdc5f6484b
commit e57166b65a
  1. 14
      upb/BUILD
  2. 49
      upb/upb/mini_table/BUILD
  3. 79
      upb/upb/mini_table/compat.c
  4. 9
      upb/upb/mini_table/compat.h
  5. 34
      upb/upb/mini_table/compat_test.cc

@ -425,14 +425,11 @@ alias(
visibility = ["//upb:friends"],
)
# begin:google_only
# alias(
# name = "mini_table_compat",
# actual = "//upb/upb/mini_table:compat",
# compatible_with = ["//buildenv/target:non_prod"],
# visibility = ["//upb:friends"],
# )
# end:google_only
alias(
name = "mini_table_compat",
actual = "//upb/upb/mini_table:compat",
visibility = ["//upb:friends"],
)
alias(
name = "mini_table_internal",
@ -559,6 +556,7 @@ upb_amalgamation(
":mini_descriptor",
":mini_descriptor_internal",
":mini_table",
":mini_table_compat",
":mini_table_internal",
":port",
":reflection",

@ -10,25 +10,24 @@ load(
"UPB_DEFAULT_COPTS",
)
# begin:google_only
# cc_library(
# name = "compat",
# srcs = [
# "compat.c",
# ],
# hdrs = [
# "compat.h",
# ],
# compatible_with = ["//buildenv/target:non_prod"],
# copts = UPB_DEFAULT_COPTS,
# visibility = ["//upb:__pkg__"],
# deps = [
# ":mini_table",
# "//upb:base",
# "//upb:port",
# ],
# )
# end:google_only
cc_library(
name = "compat",
srcs = [
"compat.c",
],
hdrs = [
"compat.h",
],
copts = UPB_DEFAULT_COPTS,
visibility = ["//visibility:public"],
deps = [
":mini_table",
"//upb:base",
"//upb:hash",
"//upb:mem",
"//upb:port",
],
)
cc_library(
name = "mini_table",
@ -78,6 +77,18 @@ cc_library(
],
)
cc_test(
name = "compat_test",
srcs = ["compat_test.cc"],
deps = [
"//upb:mini_table_compat",
"//upb/upb/test:test_messages_proto2_upb_proto",
"//upb/upb/test:test_messages_proto3_upb_proto",
"//upb/upb/test:test_upb_proto",
"@com_google_googletest//:gtest_main",
],
)
# begin:github_only
filegroup(
name = "source_files",

@ -8,51 +8,102 @@
#include "upb/upb/mini_table/compat.h"
#include "upb/upb/base/descriptor_constants.h"
#include "upb/upb/hash/common.h"
#include "upb/upb/hash/int_table.h"
#include "upb/upb/mem/arena.h"
#include "upb/upb/mini_table/field.h"
#include "upb/upb/mini_table/message.h"
// Must be last.
#include "upb/upb/port/def.inc"
static bool upb_deep_check(const upb_MiniTable* src, const upb_MiniTable* dst,
bool eq) {
if (src->field_count != dst->field_count) return false;
// Checks if source and target mini table fields are identical.
//
// If the field is a sub message and sub messages are identical we record
// the association in table.
//
// Hashing the source sub message mini table and it's equivalent in the table
// stops recursing when a cycle is detected and instead just checks if the
// destination table is equal.
static upb_MiniTableEquals_Status upb_deep_check(const upb_MiniTable* src,
const upb_MiniTable* dst,
upb_inttable* table,
upb_Arena** arena) {
if (src->field_count != dst->field_count)
return kUpb_MiniTableEquals_NotEqual;
bool marked_src = false;
for (int i = 0; i < src->field_count; i++) {
const upb_MiniTableField* src_field = &src->fields[i];
const upb_MiniTableField* dst_field =
upb_MiniTable_FindFieldByNumber(dst, src_field->number);
if (upb_MiniTableField_CType(src_field) !=
upb_MiniTableField_CType(dst_field)) return false;
upb_MiniTableField_CType(dst_field))
return false;
if (src_field->mode != dst_field->mode) return false;
if (src_field->offset != dst_field->offset) return false;
if (src_field->presence != dst_field->presence) return false;
if (src_field->UPB_PRIVATE(submsg_index) !=
dst_field->UPB_PRIVATE(submsg_index)) return false;
dst_field->UPB_PRIVATE(submsg_index))
return kUpb_MiniTableEquals_NotEqual;
// Go no further if we are only checking for compatibility.
if (!eq) continue;
if (!table) continue;
if (upb_MiniTableField_CType(src_field) == kUpb_CType_Message) {
if (!*arena) {
*arena = upb_Arena_New();
if (!upb_inttable_init(table, *arena)) {
return kUpb_MiniTableEquals_OutOfMemory;
}
}
if (!marked_src) {
marked_src = true;
upb_value val;
val.val = (uint64_t)dst;
if (!upb_inttable_insert(table, (uintptr_t)src, val, *arena)) {
return kUpb_MiniTableEquals_OutOfMemory;
}
}
const upb_MiniTable* sub_src =
upb_MiniTable_GetSubMessageTable(src, src_field);
const upb_MiniTable* sub_dst =
upb_MiniTable_GetSubMessageTable(dst, dst_field);
if (sub_src != NULL && !upb_MiniTable_Equals(sub_src, sub_dst)) {
return false;
if (sub_src != NULL) {
upb_value cmp;
if (upb_inttable_lookup(table, (uintptr_t)sub_src, &cmp)) {
// We already compared this src before. Check if same dst.
if (cmp.val != (uint64_t)sub_dst) {
return kUpb_MiniTableEquals_NotEqual;
}
} else {
// Recurse if not already visited.
upb_MiniTableEquals_Status s =
upb_deep_check(sub_src, sub_dst, table, arena);
if (s != kUpb_MiniTableEquals_Equal) {
return s;
}
}
}
}
}
return true;
return kUpb_MiniTableEquals_Equal;
}
bool upb_MiniTable_Compatible(const upb_MiniTable* src,
const upb_MiniTable* dst) {
return upb_deep_check(src, dst, false);
return upb_deep_check(src, dst, NULL, NULL);
}
bool upb_MiniTable_Equals(const upb_MiniTable* src, const upb_MiniTable* dst) {
return upb_deep_check(src, dst, true);
upb_MiniTableEquals_Status upb_MiniTable_Equals(const upb_MiniTable* src,
const upb_MiniTable* dst) {
// Arena allocated on demand for hash table.
upb_Arena* arena = NULL;
// Table to keep track of visited mini tables to guard against cycles.
upb_inttable table;
upb_MiniTableEquals_Status status = upb_deep_check(src, dst, &table, &arena);
if (arena) {
upb_Arena_Free(arena);
}
return status;
}

@ -26,8 +26,15 @@ extern "C" {
bool upb_MiniTable_Compatible(const upb_MiniTable* src,
const upb_MiniTable* dst);
typedef enum {
kUpb_MiniTableEquals_NotEqual,
kUpb_MiniTableEquals_Equal,
kUpb_MiniTableEquals_OutOfMemory,
} upb_MiniTableEquals_Status;
// Checks equality of mini tables originating from different language runtimes.
bool upb_MiniTable_Equals(const upb_MiniTable* src, const upb_MiniTable* dst);
upb_MiniTableEquals_Status upb_MiniTable_Equals(const upb_MiniTable* src,
const upb_MiniTable* dst);
#ifdef __cplusplus
} /* extern "C" */

@ -0,0 +1,34 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
/* Test of mini table accessors.
*
* Messages are created and mutated using generated code, and then
* accessed through reflective APIs exposed through mini table accessors.
*/
#include "upb/upb/mini_table/compat.h"
#include <gtest/gtest.h>
#include "google/protobuf/test_messages_proto2.upb.h"
#include "google/protobuf/test_messages_proto3.upb.h"
namespace {
TEST(GeneratedCode, EqualsTestProto2) {
EXPECT_TRUE(upb_MiniTable_Equals(
&protobuf_test_messages_proto2_ProtoWithKeywords_msg_init,
&protobuf_test_messages_proto2_ProtoWithKeywords_msg_init));
}
TEST(GeneratedCode, EqualsTestProto3) {
EXPECT_TRUE(upb_MiniTable_Equals(
&protobuf_test_messages_proto3_TestAllTypesProto3_msg_init,
&protobuf_test_messages_proto3_TestAllTypesProto3_msg_init));
}
} // namespace
Loading…
Cancel
Save