Introduce non-canonical extensions in upb messages. Minimum tagged pointer and extension APIs are added.

PiperOrigin-RevId: 936959066
This commit is contained in:
Ada Zhang 2026-06-23 16:27:18 -07:00 committed by Copybara-Service
parent 796f71274c
commit 292ec137d4
10 changed files with 197 additions and 71 deletions

View file

@ -245,9 +245,10 @@ upb_Message* _upb_Message_Copy(upb_Message* dst, const upb_Message* src,
for (size_t i = 0; i < in->size; i++) {
upb_TaggedAuxPtr tagged_ptr = in->aux_data[i];
if (upb_TaggedAuxPtr_IsExtension(tagged_ptr)) {
if (upb_TaggedAuxPtr_IsCanonicalExtension(tagged_ptr)) {
// Clone extension
const upb_Extension* msg_ext = upb_TaggedAuxPtr_Extension(tagged_ptr);
const upb_Extension* msg_ext =
upb_TaggedAuxPtr_CanonicalExtension(tagged_ptr);
const upb_MiniTableField* field = &msg_ext->ext->UPB_PRIVATE(field);
upb_Extension* dst_ext = UPB_PRIVATE(_upb_Message_GetOrCreateExtension)(
dst, msg_ext->ext, arena);
@ -267,9 +268,9 @@ upb_Message* _upb_Message_Copy(upb_Message* dst, const upb_Message* src,
}
dst_ext->data.array_val = cloned_array;
}
} else if (upb_TaggedAuxPtr_IsUnknown(tagged_ptr)) {
} else if (upb_TaggedAuxPtr_IsUnknownStringView(tagged_ptr)) {
// Clone unknown
upb_StringView* unknown = upb_TaggedAuxPtr_UnknownData(tagged_ptr);
upb_StringView* unknown = upb_TaggedPtrAux_StringViewRepr(tagged_ptr);
// Make a copy into destination arena.
if (!UPB_PRIVATE(_upb_Message_AddUnknown)(
dst, unknown->data, unknown->size, arena, kUpb_AddUnknown_Copy)) {
@ -316,12 +317,12 @@ bool upb_Message_ShallowCopy(upb_Message* dst, const upb_Message* src,
for (size_t i = 0; i < in->size; i++) {
upb_TaggedAux aux;
switch (upb_TaggedAux_Get(in->aux_data[i], &aux)) {
case kUpb_TaggedAuxType_Extension: {
case kUpb_TaggedAuxType_CanonicalExtension: {
const upb_Extension* msg_ext = aux.extension;
upb_Extension* dst_ext = upb_Arena_Malloc(arena, sizeof(upb_Extension));
if (!dst_ext) return false;
*dst_ext = *msg_ext;
dst_in->aux_data[i] = upb_TaggedAuxPtr_MakeExtension(dst_ext);
dst_in->aux_data[i] = upb_TaggedAuxPtr_MakeCanonicalExtension(dst_ext);
break;
}
case kUpb_TaggedAuxType_Unknown:
@ -329,10 +330,19 @@ bool upb_Message_ShallowCopy(upb_Message* dst, const upb_Message* src,
upb_StringView* dst_sv =
upb_Arena_Malloc(arena, sizeof(upb_StringView));
if (!dst_sv) return false;
*dst_sv = aux.unknown_data;
*dst_sv = *aux.unknown_data;
dst_in->aux_data[i] = upb_TaggedAuxPtr_MakeUnknownDataAliased(dst_sv);
break;
}
case kUpb_TaggedAuxType_NonCanonicalExtension: {
const upb_Extension* msg_ext = aux.extension;
upb_Extension* dst_ext = upb_Arena_Malloc(arena, sizeof(upb_Extension));
if (!dst_ext) return false;
*dst_ext = *msg_ext;
dst_in->aux_data[i] =
upb_TaggedAuxPtr_MakeNonCanonicalExtension(dst_ext);
break;
}
}
}

View file

@ -143,6 +143,7 @@ UPB_INLINE bool UPB_PRIVATE(_upb_Message_IsInitializedShallow)(
return (UPB_PRIVATE(_upb_MiniTable_RequiredMask)(m) & ~bits) == 0;
}
// LINT.IfChange(message_raw_fields)
UPB_INLINE void* UPB_PRIVATE(_upb_Message_MutableDataPtr)(
struct upb_Message* msg, const upb_MiniTableField* f) {
return (char*)msg + f->UPB_ONLYBITS(offset);
@ -181,6 +182,7 @@ UPB_INLINE_IF_NOT_GCC void UPB_PRIVATE(_upb_MiniTableField_DataCopy)(
}
UPB_UNREACHABLE();
}
// LINT.ThenChange(//depot/google3/third_party/upb/bits/golang/message.go:message_raw_fields)
UPB_INLINE_IF_NOT_GCC bool UPB_PRIVATE(_upb_MiniTableField_DataEquals)(
const upb_MiniTableField* f, const void* a, const void* b) {
@ -331,6 +333,19 @@ UPB_API_INLINE bool upb_Message_SetExtension(struct upb_Message* msg,
return true;
}
UPB_API_INLINE bool UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
struct upb_Message* msg, const upb_MiniTableExtension* e, const void* val,
upb_Arena* a) {
UPB_ASSERT(!upb_Message_IsFrozen(msg));
UPB_ASSERT(a);
upb_Extension* ext =
UPB_PRIVATE(_upb_Message_CreateNonCanonicalExtension)(msg, e, a);
if (!ext) return false;
UPB_PRIVATE(_upb_MiniTableField_DataCopy)
(&e->UPB_PRIVATE(field), &ext->data, val);
return true;
}
// Sets the value of the given field in the given msg. The return value is true
// if the operation completed successfully, or false if memory allocation
// failed.
@ -882,8 +897,9 @@ UPB_API_INLINE void upb_Message_ClearExtension(
if (!in) return;
for (size_t i = 0; i < in->size; i++) {
upb_TaggedAuxPtr tagged_ptr = in->aux_data[i];
if (upb_TaggedAuxPtr_IsExtension(tagged_ptr)) {
const upb_Extension* ext = upb_TaggedAuxPtr_Extension(tagged_ptr);
if (upb_TaggedAuxPtr_IsCanonicalExtension(tagged_ptr)) {
const upb_Extension* ext =
upb_TaggedAuxPtr_CanonicalExtension(tagged_ptr);
if (ext->ext == e) {
in->aux_data[i] = upb_TaggedAuxPtr_Null();
return;

View file

@ -26,8 +26,9 @@ const upb_Extension* UPB_PRIVATE(_upb_Message_Getext)(
for (size_t i = 0; i < in->size; i++) {
upb_TaggedAuxPtr tagged_ptr = in->aux_data[i];
if (upb_TaggedAuxPtr_IsExtension(tagged_ptr)) {
const upb_Extension* ext = upb_TaggedAuxPtr_Extension(tagged_ptr);
if (upb_TaggedAuxPtr_IsCanonicalExtension(tagged_ptr)) {
const upb_Extension* ext =
upb_TaggedAuxPtr_CanonicalExtension(tagged_ptr);
if (ext->ext == e) {
return ext;
}
@ -37,8 +38,9 @@ const upb_Extension* UPB_PRIVATE(_upb_Message_Getext)(
return NULL;
}
upb_Extension* UPB_PRIVATE(_upb_Message_GetOrCreateExtension)(
struct upb_Message* msg, const upb_MiniTableExtension* e, upb_Arena* a) {
UPB_INLINE upb_Extension* _upb_Message_GetOrCreateExtensionInternal(
struct upb_Message* msg, const upb_MiniTableExtension* e, upb_Arena* a,
bool canonical) {
UPB_ASSERT(!upb_Message_IsFrozen(msg));
upb_Extension* ext = (upb_Extension*)UPB_PRIVATE(_upb_Message_Getext)(msg, e);
if (ext) return ext;
@ -49,6 +51,20 @@ upb_Extension* UPB_PRIVATE(_upb_Message_GetOrCreateExtension)(
if (!ext) return NULL;
memset(ext, 0, sizeof(upb_Extension));
ext->ext = e;
in->aux_data[in->size++] = upb_TaggedAuxPtr_MakeExtension(ext);
in->aux_data[in->size++] =
canonical ? upb_TaggedAuxPtr_MakeCanonicalExtension(ext)
: upb_TaggedAuxPtr_MakeNonCanonicalExtension(ext);
return ext;
}
upb_Extension* UPB_PRIVATE(_upb_Message_GetOrCreateExtension)(
struct upb_Message* msg, const upb_MiniTableExtension* e, upb_Arena* a) {
return _upb_Message_GetOrCreateExtensionInternal(msg, e, a,
/*canonical=*/true);
}
upb_Extension* UPB_PRIVATE(_upb_Message_CreateNonCanonicalExtension)(
struct upb_Message* msg, const upb_MiniTableExtension* e, upb_Arena* a) {
return _upb_Message_GetOrCreateExtensionInternal(msg, e, a,
/*canonical=*/false);
}

View file

@ -45,6 +45,14 @@ UPB_NODISCARD upb_Extension* UPB_PRIVATE(_upb_Message_GetOrCreateExtension)(
struct upb_Message* msg, const upb_MiniTableExtension* ext,
upb_Arena* arena);
// Adds the given non-canonical extension data to the given message.
// |ext| is copied into the message instance.
// This logically replaces any previously-added extension with this number.
UPB_NODISCARD upb_Extension* UPB_PRIVATE(
_upb_Message_CreateNonCanonicalExtension)(struct upb_Message* msg,
const upb_MiniTableExtension* ext,
upb_Arena* arena);
// Returns an extension for a message with a given mini table,
// or NULL if no extension exists with this mini table.
const upb_Extension* UPB_PRIVATE(_upb_Message_Getext)(

View file

@ -42,15 +42,27 @@ extern const double kUpb_NaN;
// Internal members of a upb_Message that track unknown fields and/or
// extensions. We can change this without breaking binary compatibility.
// LINT.IfChange(tagged_aux_type)
typedef struct upb_TaggedAuxPtr {
// Two lowest bits form a tag:
// 00 - non-aliased unknown data
// 10 - aliased unknown data
// 01 - extension
// Three lowest bits form a tag:
// 000 - non-aliased unknown data (upb_StringView*)
// 100 - aliased unknown data (upb_StringView*)
// 001 - non-canonical extension (upb_Extension*)
// 011 - canonical extension (upb_Extension*)
//
// The main semantic difference between aliased and non-aliased unknown data
// is that non-aliased unknown data can be assumed to have the following
// layout:
// Bit 0 (lowest bit): Represents the data format in memory (1 for parsed
// form, 0 for serialized form).
// Bit 1 (middle bit): Represents whether the data is semantically known or
// not (1 for known, 0 for unknown).
// Bit 2 (highest bit): Aliased/Non-aliased (1 for aliased, 0 for
// non-aliased).
//
// Following this tag structure, we can later use tag `010` for lazy
// extensions.
//
// The main semantic difference between aliased and non-aliased
// unknown data is that non-aliased unknown data can be assumed to have the
// following layout:
//
// [upb_StringView] [data]
//
@ -64,55 +76,75 @@ typedef struct upb_TaggedAuxPtr {
// For aliased unknown data, this layout is _not_ guaranteed, since the
// pointer to the StringView can be anywhere in the allocation, and the
// StringView may point to non-data memory.
//
// For a non-canonical extension, its schema is known but not
// the one expected by the message, so it should be treated like an unknown
// field, but is stored as an extension to lazily defer serialization.
uintptr_t ptr;
} upb_TaggedAuxPtr;
UPB_INLINE bool upb_TaggedAuxPtr_IsExtension(upb_TaggedAuxPtr ptr) {
return ptr.ptr & 1;
// If this returns true, then the entry is semantically known (but may be in
// either parsed or unparsed form).
UPB_INLINE bool upb_TaggedAuxPtr_IsSemanticallyKnown(upb_TaggedAuxPtr ptr) {
return (ptr.ptr & 0x2) != 0;
}
UPB_INLINE bool upb_TaggedAuxPtr_IsUnknown(upb_TaggedAuxPtr ptr) {
return (ptr.ptr != 0) && ((ptr.ptr & 1) == 0);
UPB_INLINE bool upb_TaggedAuxPtr_IsCanonicalExtension(upb_TaggedAuxPtr ptr) {
return (ptr.ptr & 3) == 3;
}
UPB_INLINE bool upb_TaggedAuxPtr_IsNonCanonicalExtension(upb_TaggedAuxPtr ptr) {
return (ptr.ptr & 3) == 1;
}
// Returns true if the entry is aliased/non-aliased unknown data.
UPB_INLINE bool upb_TaggedAuxPtr_IsUnknownStringView(upb_TaggedAuxPtr ptr) {
return (ptr.ptr != 0) && ((ptr.ptr & 3) == 0);
}
UPB_INLINE bool upb_TaggedAuxPtr_IsUnknownAliased(upb_TaggedAuxPtr ptr) {
return (ptr.ptr != 0) && ((ptr.ptr & 2) == 2);
return (ptr.ptr != 0) && ((ptr.ptr & 5) == 4);
}
UPB_INLINE upb_Extension* upb_TaggedAuxPtr_Extension(upb_TaggedAuxPtr ptr) {
UPB_ASSERT(upb_TaggedAuxPtr_IsExtension(ptr));
return (upb_Extension*)(ptr.ptr & ~3ULL);
UPB_INLINE upb_Extension* upb_TaggedAuxPtr_CanonicalExtension(
upb_TaggedAuxPtr ptr) {
UPB_ASSERT(upb_TaggedAuxPtr_IsCanonicalExtension(ptr));
return (upb_Extension*)(ptr.ptr & ~7ULL);
}
UPB_INLINE upb_StringView* upb_TaggedAuxPtr_UnknownData(upb_TaggedAuxPtr ptr) {
UPB_ASSERT(!upb_TaggedAuxPtr_IsExtension(ptr));
return (upb_StringView*)(ptr.ptr & ~3ULL);
UPB_INLINE upb_Extension* upb_TaggedAuxPtr_NonCanonicalExtension(
upb_TaggedAuxPtr ptr) {
UPB_ASSERT(upb_TaggedAuxPtr_IsNonCanonicalExtension(ptr));
return (upb_Extension*)(ptr.ptr & ~7ULL);
}
// Returns a pointer to the aliased or unaliased unknown upb_StringView* data.
UPB_INLINE upb_StringView* upb_TaggedPtrAux_StringViewRepr(
upb_TaggedAuxPtr ptr) {
UPB_ASSERT(upb_TaggedAuxPtr_IsUnknownStringView(ptr));
return (upb_StringView*)(ptr.ptr & ~7ULL);
}
// LINT.ThenChange(//depot/google3/third_party/upb/bits/golang/message.go:tagged_aux_type)
typedef enum {
kUpb_TaggedAuxType_Extension,
kUpb_TaggedAuxType_Unknown,
kUpb_TaggedAuxType_AliasedUnknown
kUpb_TaggedAuxType_Unknown = 0, // tag 000
kUpb_TaggedAuxType_NonCanonicalExtension = 1, // tag 001
kUpb_TaggedAuxType_CanonicalExtension = 3, // tag 011
kUpb_TaggedAuxType_AliasedUnknown = 4 // tag 100
} upb_TaggedAuxType;
typedef union {
upb_Extension* extension;
upb_StringView unknown_data;
const upb_StringView* unknown_data;
} upb_TaggedAux;
UPB_INLINE upb_TaggedAuxType upb_TaggedAux_Get(upb_TaggedAuxPtr ptr,
upb_TaggedAux* data) {
if (upb_TaggedAuxPtr_IsExtension(ptr)) {
data->extension = upb_TaggedAuxPtr_Extension(ptr);
return kUpb_TaggedAuxType_Extension;
} else if (upb_TaggedAuxPtr_IsUnknownAliased(ptr)) {
data->unknown_data = *upb_TaggedAuxPtr_UnknownData(ptr);
return kUpb_TaggedAuxType_AliasedUnknown;
} else {
UPB_ASSERT(upb_TaggedAuxPtr_IsUnknown(ptr));
data->unknown_data = *upb_TaggedAuxPtr_UnknownData(ptr);
return kUpb_TaggedAuxType_Unknown;
}
uintptr_t untagged = ptr.ptr & ~7ULL;
UPB_ASSERT((untagged & 7) == 0);
memcpy(data, &untagged, sizeof(*data));
return (upb_TaggedAuxType)(ptr.ptr & 7);
}
UPB_INLINE upb_TaggedAuxPtr upb_TaggedAuxPtr_Null(void) {
@ -122,7 +154,16 @@ UPB_INLINE upb_TaggedAuxPtr upb_TaggedAuxPtr_Null(void) {
}
UPB_INLINE upb_TaggedAuxPtr
upb_TaggedAuxPtr_MakeExtension(const upb_Extension* e) {
upb_TaggedAuxPtr_MakeCanonicalExtension(const upb_Extension* e) {
UPB_ASSERT(((uintptr_t)e & 7) == 0);
upb_TaggedAuxPtr ptr;
ptr.ptr = (uintptr_t)e | 3;
return ptr;
}
UPB_INLINE upb_TaggedAuxPtr
upb_TaggedAuxPtr_MakeNonCanonicalExtension(const upb_Extension* e) {
UPB_ASSERT(((uintptr_t)e & 7) == 0);
upb_TaggedAuxPtr ptr;
ptr.ptr = (uintptr_t)e | 1;
return ptr;
@ -133,6 +174,7 @@ upb_TaggedAuxPtr_MakeExtension(const upb_Extension* e) {
// view.
UPB_INLINE upb_TaggedAuxPtr
upb_TaggedAuxPtr_MakeUnknownData(const upb_StringView* sv) {
UPB_ASSERT(((uintptr_t)sv & 7) == 0);
upb_TaggedAuxPtr ptr;
ptr.ptr = (uintptr_t)sv;
return ptr;
@ -142,8 +184,9 @@ upb_TaggedAuxPtr_MakeUnknownData(const upb_StringView* sv) {
// the data it points to.
UPB_INLINE upb_TaggedAuxPtr
upb_TaggedAuxPtr_MakeUnknownDataAliased(const upb_StringView* sv) {
UPB_ASSERT(((uintptr_t)sv & 7) == 0);
upb_TaggedAuxPtr ptr;
ptr.ptr = (uintptr_t)sv | 2;
ptr.ptr = (uintptr_t)sv | 4;
return ptr;
}
@ -265,8 +308,8 @@ UPB_NODISCARD UPB_INLINE bool UPB_PRIVATE(
upb_Message_Internal* in = UPB_PRIVATE(_upb_Message_GetInternal)(msg);
if (in && in->size) {
upb_TaggedAuxPtr ptr = in->aux_data[in->size - 1];
if (upb_TaggedAuxPtr_IsUnknown(ptr)) {
upb_StringView* existing = upb_TaggedAuxPtr_UnknownData(ptr);
if (upb_TaggedAuxPtr_IsUnknownStringView(ptr)) {
upb_StringView* existing = upb_TaggedPtrAux_StringViewRepr(ptr);
// Fast path if the field we're adding is immediately after the last
// added unknown field.
//
@ -329,8 +372,8 @@ UPB_INLINE bool upb_Message_NextUnknown(const struct upb_Message* msg,
if (in) {
while (i < in->size) {
upb_TaggedAuxPtr tagged_ptr = in->aux_data[i++];
if (upb_TaggedAuxPtr_IsUnknown(tagged_ptr)) {
*data = *upb_TaggedAuxPtr_UnknownData(tagged_ptr);
if (upb_TaggedAuxPtr_IsUnknownStringView(tagged_ptr)) {
*data = *upb_TaggedPtrAux_StringViewRepr(tagged_ptr);
*iter = i;
return true;
}
@ -357,8 +400,9 @@ UPB_INLINE bool upb_Message_NextExtension(const struct upb_Message* msg,
if (in) {
while (i < in->size) {
upb_TaggedAuxPtr tagged_ptr = in->aux_data[i++];
if (upb_TaggedAuxPtr_IsExtension(tagged_ptr)) {
const upb_Extension* ext = upb_TaggedAuxPtr_Extension(tagged_ptr);
if (upb_TaggedAuxPtr_IsCanonicalExtension(tagged_ptr)) {
const upb_Extension* ext =
upb_TaggedAuxPtr_CanonicalExtension(tagged_ptr);
// Empty repeated fields or maps semantically don't exist.
if (UPB_PRIVATE(_upb_Extension_IsEmpty)(ext)) continue;
@ -385,10 +429,10 @@ UPB_INLINE bool UPB_PRIVATE(_upb_Message_NextExtensionReverse)(
while (i < size) {
upb_TaggedAuxPtr tagged_ptr = in->aux_data[size - 1 - i];
i++;
if (!upb_TaggedAuxPtr_IsExtension(tagged_ptr)) {
if (!upb_TaggedAuxPtr_IsCanonicalExtension(tagged_ptr)) {
continue;
}
const upb_Extension* ext = upb_TaggedAuxPtr_Extension(tagged_ptr);
const upb_Extension* ext = upb_TaggedAuxPtr_CanonicalExtension(tagged_ptr);
// Empty repeated fields or maps semantically don't exist.
if (UPB_PRIVATE(_upb_Extension_IsEmpty)(ext)) continue;

View file

@ -167,7 +167,7 @@ bool _upb_mapsorter_pushexts(_upb_mapsorter* s, const upb_Message_Internal* in,
_upb_sortedmap* sorted) {
size_t count = 0;
for (size_t i = 0; i < in->size; i++) {
count += upb_TaggedAuxPtr_IsExtension(in->aux_data[i]);
count += upb_TaggedAuxPtr_IsCanonicalExtension(in->aux_data[i]);
}
if (!_upb_mapsorter_resize(s, sorted, count)) return false;
if (count == 0) return true;
@ -175,8 +175,8 @@ bool _upb_mapsorter_pushexts(_upb_mapsorter* s, const upb_Message_Internal* in,
(const upb_Extension**)&s->entries[sorted->start];
for (size_t i = 0; i < in->size; i++) {
upb_TaggedAuxPtr tagged_ptr = in->aux_data[i];
if (upb_TaggedAuxPtr_IsExtension(tagged_ptr)) {
*entry++ = upb_TaggedAuxPtr_Extension(tagged_ptr);
if (upb_TaggedAuxPtr_IsCanonicalExtension(tagged_ptr)) {
*entry++ = upb_TaggedAuxPtr_CanonicalExtension(tagged_ptr);
}
}
qsort(&s->entries[sorted->start], count, sizeof(*s->entries),

View file

@ -46,8 +46,8 @@ UPB_NOINLINE bool UPB_PRIVATE(_upb_Message_AddUnknownSlowPath)(upb_Message* msg,
// this one
if (!alias && in && in->size) {
upb_TaggedAuxPtr ptr = in->aux_data[in->size - 1];
if (upb_TaggedAuxPtr_IsUnknown(ptr)) {
upb_StringView* existing = upb_TaggedAuxPtr_UnknownData(ptr);
if (upb_TaggedAuxPtr_IsUnknownStringView(ptr)) {
upb_StringView* existing = upb_TaggedPtrAux_StringViewRepr(ptr);
if (!upb_TaggedAuxPtr_IsUnknownAliased(ptr)) {
// If part of the existing field was deleted at the beginning, we can
// reconstruct it by comparing the address of the end with the address
@ -114,8 +114,8 @@ bool UPB_PRIVATE(_upb_Message_AddUnknownV)(struct upb_Message* msg,
upb_Message_Internal* in = UPB_PRIVATE(_upb_Message_GetInternal)(msg);
if (in && in->size) {
upb_TaggedAuxPtr ptr = in->aux_data[in->size - 1];
if (upb_TaggedAuxPtr_IsUnknown(ptr)) {
upb_StringView* existing = upb_TaggedAuxPtr_UnknownData(ptr);
if (upb_TaggedAuxPtr_IsUnknownStringView(ptr)) {
upb_StringView* existing = upb_TaggedPtrAux_StringViewRepr(ptr);
if (!upb_TaggedAuxPtr_IsUnknownAliased(ptr)) {
size_t prev_alloc_size =
(existing->data + existing->size) - (char*)existing;
@ -164,7 +164,7 @@ void _upb_Message_DiscardUnknown_shallow(upb_Message* msg) {
uint32_t size = 0;
for (uint32_t i = 0; i < in->size; i++) {
upb_TaggedAuxPtr tagged_ptr = in->aux_data[i];
if (upb_TaggedAuxPtr_IsExtension(tagged_ptr)) {
if (upb_TaggedAuxPtr_IsSemanticallyKnown(tagged_ptr)) {
in->aux_data[size++] = tagged_ptr;
}
}
@ -181,8 +181,8 @@ upb_Message_DeleteUnknownStatus upb_Message_DeleteUnknown(upb_Message* msg,
UPB_ASSERT(in);
UPB_ASSERT(*iter <= in->size);
upb_TaggedAuxPtr unknown_ptr = in->aux_data[*iter - 1];
UPB_ASSERT(upb_TaggedAuxPtr_IsUnknown(unknown_ptr));
upb_StringView* unknown = upb_TaggedAuxPtr_UnknownData(unknown_ptr);
UPB_ASSERT(upb_TaggedAuxPtr_IsUnknownStringView(unknown_ptr));
upb_StringView* unknown = upb_TaggedPtrAux_StringViewRepr(unknown_ptr);
if (unknown->data == data->data && unknown->size == data->size) {
// Remove whole field
in->aux_data[*iter - 1] = upb_TaggedAuxPtr_Null();
@ -286,10 +286,13 @@ void upb_Message_Freeze(upb_Message* msg, const upb_MiniTable* m) {
uint32_t size = in ? in->size : 0;
for (size_t i = 0; i < size; i++) {
upb_TaggedAuxPtr tagged_ptr = in->aux_data[i];
if (!upb_TaggedAuxPtr_IsExtension(tagged_ptr)) {
upb_TaggedAux aux;
upb_TaggedAuxType type = upb_TaggedAux_Get(tagged_ptr, &aux);
if (type != kUpb_TaggedAuxType_CanonicalExtension &&
type != kUpb_TaggedAuxType_NonCanonicalExtension) {
continue;
}
const upb_Extension* ext = upb_TaggedAuxPtr_Extension(tagged_ptr);
const upb_Extension* ext = aux.extension;
const upb_MiniTableExtension* e = ext->ext;
const upb_MiniTableField* f = &e->UPB_PRIVATE(field);
const upb_MiniTable* m2 = upb_MiniTableExtension_GetSubMessage(e);

View file

@ -34,6 +34,7 @@
#include "upb/message/accessors.h"
#include "upb/message/array.h"
#include "upb/message/compare.h"
#include "upb/message/internal/accessors.h"
#include "upb/message/map.h"
#include "upb/message/message.h"
#include "upb/message/test.upb.h"
@ -877,6 +878,27 @@ TEST(MessageTest, Freeze) {
ASSERT_TRUE(upb_Map_IsFrozen(map));
ASSERT_TRUE(upb_Message_IsFrozen(UPB_UPCAST(nest)));
}
{
upb_test_TestExtensions* ext_msg = upb_test_TestExtensions_new(arena.ptr());
upb_Message* msg = UPB_UPCAST(ext_msg);
protobuf_test_messages_proto3_TestAllTypesProto3* submsg =
protobuf_test_messages_proto3_TestAllTypesProto3_new(arena.ptr());
upb_Message* submsg_upcast = UPB_UPCAST(submsg);
ASSERT_FALSE(upb_Message_IsFrozen(msg));
ASSERT_FALSE(upb_Message_IsFrozen(submsg_upcast));
// Set a non-canonical extension containing a sub-message.
bool set_ext_ok = UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
msg, upb_test_optional_msg_ext_ext, &submsg, arena.ptr());
ASSERT_TRUE(set_ext_ok);
// Freezing the parent message should recursively freeze the non-canonical
// extension's sub-message.
upb_Message_Freeze(msg, &upb_0test__TestExtensions_msg_init);
ASSERT_TRUE(upb_Message_IsFrozen(msg));
ASSERT_TRUE(upb_Message_IsFrozen(submsg_upcast));
}
}
/* Tests some somewhat tricky math used in size calculations while encoding */

View file

@ -23,6 +23,12 @@ struct upb_MiniTableExtension {
struct upb_MiniTableField UPB_PRIVATE(field);
union upb_MiniTableSub UPB_PRIVATE(sub); // NULL unless submsg or proto2 enum
// A known extendee schema for a canonical extension. For a non-canonical
// extension, it's typically converted from a canonical extension via the
// upb_Message_Convert() API, but is not registered on the extension
// registry of the target message when it gets converted. In this case, the
// `extendee` info is present on the source message before conversion.
const struct upb_MiniTable* UPB_PRIVATE(extendee);
};

View file

@ -177,8 +177,9 @@ bool upb_Message_Next(const upb_Message* msg, const upb_MessageDef* m,
for (; (i - n) < in->size; i++) {
upb_TaggedAuxPtr tagged_ptr = in->aux_data[i - n];
if (upb_TaggedAuxPtr_IsExtension(tagged_ptr)) {
const upb_Extension* ext = upb_TaggedAuxPtr_Extension(tagged_ptr);
if (upb_TaggedAuxPtr_IsCanonicalExtension(tagged_ptr)) {
const upb_Extension* ext =
upb_TaggedAuxPtr_CanonicalExtension(tagged_ptr);
memcpy(out_val, &ext->data, sizeof(*out_val));
*out_f = upb_DefPool_FindExtensionByMiniTable(ext_pool, ext->ext);
*iter = i;