diff --git a/php/ext/google/protobuf/php-upb.c b/php/ext/google/protobuf/php-upb.c index 9b13b7f4d0..4d866a5b33 100644 --- a/php/ext/google/protobuf/php-upb.c +++ b/php/ext/google/protobuf/php-upb.c @@ -8420,23 +8420,6 @@ const upb_MiniTableExtension* upb_ExtensionRegistry_Lookup( } -#include - -// Must be last. - -// A MiniTable for an empty message, used for unlinked sub-messages. -const struct upb_MiniTable UPB_PRIVATE(_kUpb_MiniTable_Empty) = { - .UPB_PRIVATE(subs) = NULL, - .UPB_PRIVATE(fields) = NULL, - .UPB_PRIVATE(size) = 0, - .UPB_PRIVATE(field_count) = 0, - .UPB_PRIVATE(ext) = kUpb_ExtMode_NonExtendable, - .UPB_PRIVATE(dense_below) = 0, - .UPB_PRIVATE(table_mask) = -1, - .UPB_PRIVATE(required_count) = 0, -}; - - #include #include #include @@ -8504,6 +8487,106 @@ bool upb_MiniTable_NextOneofField(const upb_MiniTable* m, } +#include +#include + + +// Must be last. + +// 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->UPB_PRIVATE(field_count) != dst->UPB_PRIVATE(field_count)) + return kUpb_MiniTableEquals_NotEqual; + bool marked_src = false; + for (int i = 0; i < upb_MiniTable_FieldCount(src); i++) { + const upb_MiniTableField* src_field = upb_MiniTable_GetFieldByIndex(src, i); + const upb_MiniTableField* dst_field = upb_MiniTable_FindFieldByNumber( + dst, upb_MiniTableField_Number(src_field)); + + if (upb_MiniTableField_CType(src_field) != + upb_MiniTableField_CType(dst_field)) + return false; + if (src_field->UPB_PRIVATE(mode) != dst_field->UPB_PRIVATE(mode)) + return false; + if (src_field->UPB_PRIVATE(offset) != dst_field->UPB_PRIVATE(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 kUpb_MiniTableEquals_NotEqual; + + // Go no further if we are only checking for compatibility. + 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_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 kUpb_MiniTableEquals_Equal; +} + +bool upb_MiniTable_Compatible(const upb_MiniTable* src, + const upb_MiniTable* dst) { + return upb_deep_check(src, dst, NULL, NULL); +} + +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; +} + + // Must be last. @@ -15792,6 +15875,23 @@ const char* UPB_PRIVATE(_upb_WireReader_SkipGroup)( return ptr; } + +#include + +// Must be last. + +// A MiniTable for an empty message, used for unlinked sub-messages. +const struct upb_MiniTable UPB_PRIVATE(_kUpb_MiniTable_Empty) = { + .UPB_PRIVATE(subs) = NULL, + .UPB_PRIVATE(fields) = NULL, + .UPB_PRIVATE(size) = 0, + .UPB_PRIVATE(field_count) = 0, + .UPB_PRIVATE(ext) = kUpb_ExtMode_NonExtendable, + .UPB_PRIVATE(dense_below) = 0, + .UPB_PRIVATE(table_mask) = -1, + .UPB_PRIVATE(required_count) = 0, +}; + // This should #undef all macros #defined in def.inc #undef UPB_SIZE diff --git a/php/ext/google/protobuf/php-upb.h b/php/ext/google/protobuf/php-upb.h index eb716f45c4..6dd35f57d4 100644 --- a/php/ext/google/protobuf/php-upb.h +++ b/php/ext/google/protobuf/php-upb.h @@ -12986,6 +12986,42 @@ char* upb_MtDataEncoder_EncodeMessageSet(upb_MtDataEncoder* e, char* ptr); #endif /* UPB_MINI_DESCRIPTOR_INTERNAL_ENCODE_H_ */ +#ifndef UPB_MINI_TABLE_COMPAT_H_ +#define UPB_MINI_TABLE_COMPAT_H_ + + +// Must be last. + +// upb does not support mixing minitables from different sources but these +// functions are still used by some existing users so for now we make them +// available here. This may or may not change in the future so do not add +// them to new code. + +#ifdef __cplusplus +extern "C" { +#endif + +// Checks if memory layout of src is compatible with dst. +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. +upb_MiniTableEquals_Status upb_MiniTable_Equals(const upb_MiniTable* src, + const upb_MiniTable* dst); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* UPB_MINI_TABLE_COMPAT_H_ */ + #ifndef UPB_REFLECTION_DEF_BUILDER_INTERNAL_H_ #define UPB_REFLECTION_DEF_BUILDER_INTERNAL_H_ diff --git a/ruby/ext/google/protobuf_c/ruby-upb.c b/ruby/ext/google/protobuf_c/ruby-upb.c index 89a20f3d56..07f17c3ff6 100644 --- a/ruby/ext/google/protobuf_c/ruby-upb.c +++ b/ruby/ext/google/protobuf_c/ruby-upb.c @@ -7936,23 +7936,6 @@ const upb_MiniTableExtension* upb_ExtensionRegistry_Lookup( } -#include - -// Must be last. - -// A MiniTable for an empty message, used for unlinked sub-messages. -const struct upb_MiniTable UPB_PRIVATE(_kUpb_MiniTable_Empty) = { - .UPB_PRIVATE(subs) = NULL, - .UPB_PRIVATE(fields) = NULL, - .UPB_PRIVATE(size) = 0, - .UPB_PRIVATE(field_count) = 0, - .UPB_PRIVATE(ext) = kUpb_ExtMode_NonExtendable, - .UPB_PRIVATE(dense_below) = 0, - .UPB_PRIVATE(table_mask) = -1, - .UPB_PRIVATE(required_count) = 0, -}; - - #include #include #include @@ -8020,6 +8003,106 @@ bool upb_MiniTable_NextOneofField(const upb_MiniTable* m, } +#include +#include + + +// Must be last. + +// 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->UPB_PRIVATE(field_count) != dst->UPB_PRIVATE(field_count)) + return kUpb_MiniTableEquals_NotEqual; + bool marked_src = false; + for (int i = 0; i < upb_MiniTable_FieldCount(src); i++) { + const upb_MiniTableField* src_field = upb_MiniTable_GetFieldByIndex(src, i); + const upb_MiniTableField* dst_field = upb_MiniTable_FindFieldByNumber( + dst, upb_MiniTableField_Number(src_field)); + + if (upb_MiniTableField_CType(src_field) != + upb_MiniTableField_CType(dst_field)) + return false; + if (src_field->UPB_PRIVATE(mode) != dst_field->UPB_PRIVATE(mode)) + return false; + if (src_field->UPB_PRIVATE(offset) != dst_field->UPB_PRIVATE(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 kUpb_MiniTableEquals_NotEqual; + + // Go no further if we are only checking for compatibility. + 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_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 kUpb_MiniTableEquals_Equal; +} + +bool upb_MiniTable_Compatible(const upb_MiniTable* src, + const upb_MiniTable* dst) { + return upb_deep_check(src, dst, NULL, NULL); +} + +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; +} + + // Must be last. @@ -15308,6 +15391,23 @@ const char* UPB_PRIVATE(_upb_WireReader_SkipGroup)( return ptr; } + +#include + +// Must be last. + +// A MiniTable for an empty message, used for unlinked sub-messages. +const struct upb_MiniTable UPB_PRIVATE(_kUpb_MiniTable_Empty) = { + .UPB_PRIVATE(subs) = NULL, + .UPB_PRIVATE(fields) = NULL, + .UPB_PRIVATE(size) = 0, + .UPB_PRIVATE(field_count) = 0, + .UPB_PRIVATE(ext) = kUpb_ExtMode_NonExtendable, + .UPB_PRIVATE(dense_below) = 0, + .UPB_PRIVATE(table_mask) = -1, + .UPB_PRIVATE(required_count) = 0, +}; + // This should #undef all macros #defined in def.inc #undef UPB_SIZE diff --git a/ruby/ext/google/protobuf_c/ruby-upb.h b/ruby/ext/google/protobuf_c/ruby-upb.h index 46a568f0dd..244da19429 100755 --- a/ruby/ext/google/protobuf_c/ruby-upb.h +++ b/ruby/ext/google/protobuf_c/ruby-upb.h @@ -12758,6 +12758,42 @@ char* upb_MtDataEncoder_EncodeMessageSet(upb_MtDataEncoder* e, char* ptr); #endif /* UPB_MINI_DESCRIPTOR_INTERNAL_ENCODE_H_ */ +#ifndef UPB_MINI_TABLE_COMPAT_H_ +#define UPB_MINI_TABLE_COMPAT_H_ + + +// Must be last. + +// upb does not support mixing minitables from different sources but these +// functions are still used by some existing users so for now we make them +// available here. This may or may not change in the future so do not add +// them to new code. + +#ifdef __cplusplus +extern "C" { +#endif + +// Checks if memory layout of src is compatible with dst. +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. +upb_MiniTableEquals_Status upb_MiniTable_Equals(const upb_MiniTable* src, + const upb_MiniTable* dst); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* UPB_MINI_TABLE_COMPAT_H_ */ + #ifndef UPB_REFLECTION_DEF_POOL_INTERNAL_H_ #define UPB_REFLECTION_DEF_POOL_INTERNAL_H_