Removed obsolete experimental code for tagged-ptr field promotion.

PiperOrigin-RevId: 823526718
This commit is contained in:
Joshua Haberman 2025-10-24 07:45:03 -07:00 committed by Copybara-Service
parent 89fb5ebf4b
commit 83d1db468b
22 changed files with 64 additions and 914 deletions

View file

@ -181,7 +181,6 @@ upb/message/map.h
upb/message/map_gencode_util.h
upb/message/merge.h
upb/message/message.h
upb/message/tagged_ptr.h
upb/message/value.h
upb/mini_descriptor/build_enum.h
upb/mini_descriptor/decode.h

View file

@ -36,7 +36,6 @@ pub enum DecodeStatus {
BadUtf8 = 3,
MaxDepthExceeded = 4,
MissingRequired = 5,
UnlinkedSubMessage = 6,
}
// LINT.ThenChange()

View file

@ -38,7 +38,6 @@ cc_library(
"map.h",
"map_gencode_util.h",
"message.h",
"tagged_ptr.h",
"value.h",
],
copts = UPB_DEFAULT_COPTS,
@ -84,7 +83,6 @@ cc_library(
"internal/map.h",
"internal/map_sorter.h",
"internal/message.h",
"internal/tagged_ptr.h",
],
copts = UPB_DEFAULT_COPTS,
visibility = ["//visibility:public"],

View file

@ -16,7 +16,6 @@
#include "upb/message/internal/accessors.h"
#include "upb/message/map.h"
#include "upb/message/message.h"
#include "upb/message/tagged_ptr.h"
#include "upb/message/value.h"
#include "upb/mini_table/extension.h"
#include "upb/mini_table/field.h"
@ -57,10 +56,6 @@ UPB_API_INLINE upb_MessageValue
upb_Message_GetField(const upb_Message* msg, const upb_MiniTableField* f,
upb_MessageValue default_val);
UPB_API_INLINE upb_TaggedMessagePtr upb_Message_GetTaggedMessagePtr(
const upb_Message* msg, const upb_MiniTableField* field,
upb_Message* default_val);
UPB_API_INLINE const upb_Array* upb_Message_GetArray(
const upb_Message* msg, const upb_MiniTableField* f);

View file

@ -23,7 +23,6 @@
#include "upb/message/internal/message.h"
#include "upb/message/map.h"
#include "upb/message/message.h"
#include "upb/message/tagged_ptr.h"
#include "upb/mini_table/extension.h"
#include "upb/mini_table/field.h"
#include "upb/mini_table/internal/field.h"
@ -72,14 +71,11 @@ static bool upb_Clone_MessageValue(void* value, upb_CType value_type,
return true;
} break;
case kUpb_CType_Message: {
const upb_TaggedMessagePtr source = *(upb_TaggedMessagePtr*)value;
bool is_empty = upb_TaggedMessagePtr_IsEmpty(source);
if (is_empty) sub = UPB_PRIVATE(_upb_MiniTable_Empty)();
UPB_ASSERT(sub);
const upb_Message* source = *(upb_Message**)value;
UPB_ASSERT(source);
upb_Message* clone = upb_Message_DeepClone(
UPB_PRIVATE(_upb_TaggedMessagePtr_GetMessage)(source), sub, arena);
*(upb_TaggedMessagePtr*)value =
UPB_PRIVATE(_upb_TaggedMessagePtr_Pack)(clone, is_empty);
upb_Message* clone = upb_Message_DeepClone(source, sub, arena);
*(upb_Message**)value = clone;
return clone != NULL;
} break;
}
@ -199,27 +195,16 @@ upb_Message* _upb_Message_Copy(upb_Message* dst, const upb_Message* src,
if (upb_MiniTableField_IsScalar(field)) {
switch (upb_MiniTableField_CType(field)) {
case kUpb_CType_Message: {
upb_TaggedMessagePtr tagged =
upb_Message_GetTaggedMessagePtr(src, field, NULL);
const upb_Message* sub_message =
UPB_PRIVATE(_upb_TaggedMessagePtr_GetMessage)(tagged);
const upb_Message* sub_message = upb_Message_GetMessage(src, field);
if (sub_message != NULL) {
// If the message is currently in an unlinked, "empty" state we keep
// it that way, because we don't want to deal with decode options,
// decode status, or possible parse failure here.
bool is_empty = upb_TaggedMessagePtr_IsEmpty(tagged);
const upb_MiniTable* sub_message_table =
is_empty ? UPB_PRIVATE(_upb_MiniTable_Empty)()
: upb_MiniTable_GetSubMessageTable(mini_table, field);
upb_MiniTable_GetSubMessageTable(mini_table, field);
upb_Message* dst_sub_message =
upb_Message_DeepClone(sub_message, sub_message_table, arena);
if (dst_sub_message == NULL) {
return NULL;
}
UPB_PRIVATE(_upb_Message_SetTaggedMessagePtr)
(dst, field,
UPB_PRIVATE(_upb_TaggedMessagePtr_Pack)(dst_sub_message,
is_empty));
upb_Message_SetBaseFieldMessage(dst, field, dst_sub_message);
}
} break;
case kUpb_CType_String:

View file

@ -20,7 +20,6 @@
#include "upb/message/internal/extension.h"
#include "upb/message/internal/map.h"
#include "upb/message/internal/message.h"
#include "upb/message/internal/tagged_ptr.h"
#include "upb/message/internal/types.h"
#include "upb/message/value.h"
#include "upb/mini_table/enum.h"
@ -417,57 +416,24 @@ UPB_API_INLINE int64_t upb_Message_GetInt64(const struct upb_Message* msg,
return upb_Message_GetField(msg, f, def).int64_val;
}
UPB_INLINE void UPB_PRIVATE(_upb_Message_AssertMapIsUntagged)(
const struct upb_Message* msg, const upb_MiniTableField* field) {
UPB_UNUSED(msg);
UPB_PRIVATE(_upb_MiniTableField_CheckIsMap)(field);
#ifndef NDEBUG
uintptr_t default_val = 0;
uintptr_t tagged;
_upb_Message_GetNonExtensionField(msg, field, &default_val, &tagged);
UPB_ASSERT(!upb_TaggedMessagePtr_IsEmpty(tagged));
#endif
}
UPB_API_INLINE const struct upb_Map* upb_Message_GetMap(
const struct upb_Message* msg, const upb_MiniTableField* f) {
UPB_PRIVATE(_upb_MiniTableField_CheckIsMap)(f);
UPB_PRIVATE(_upb_Message_AssertMapIsUntagged)(msg, f);
struct upb_Map* ret;
const struct upb_Map* default_val = NULL;
_upb_Message_GetNonExtensionField(msg, f, &default_val, &ret);
return ret;
}
UPB_API_INLINE uintptr_t upb_Message_GetTaggedMessagePtr(
const struct upb_Message* msg, const upb_MiniTableField* f,
struct upb_Message* default_val) {
UPB_ASSUME(upb_MiniTableField_CType(f) == kUpb_CType_Message);
UPB_ASSUME(UPB_PRIVATE(_upb_MiniTableField_GetRep)(f) ==
UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte));
UPB_ASSUME(upb_MiniTableField_IsScalar(f));
uintptr_t tagged;
_upb_Message_GetNonExtensionField(msg, f, &default_val, &tagged);
return tagged;
}
// For internal use only; users cannot set tagged messages because only the
// parser and the message copier are allowed to directly create an empty
// message.
UPB_INLINE void UPB_PRIVATE(_upb_Message_SetTaggedMessagePtr)(
struct upb_Message* msg, const upb_MiniTableField* f,
uintptr_t sub_message) {
UPB_ASSUME(upb_MiniTableField_CType(f) == kUpb_CType_Message);
UPB_ASSUME(UPB_PRIVATE(_upb_MiniTableField_GetRep)(f) ==
UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte));
UPB_ASSUME(upb_MiniTableField_IsScalar(f));
upb_Message_SetBaseField(msg, f, &sub_message);
}
UPB_API_INLINE const struct upb_Message* upb_Message_GetMessage(
const struct upb_Message* msg, const upb_MiniTableField* f) {
uintptr_t tagged = upb_Message_GetTaggedMessagePtr(msg, f, NULL);
return upb_TaggedMessagePtr_GetNonEmptyMessage(tagged);
UPB_ASSUME(upb_MiniTableField_CType(f) == kUpb_CType_Message);
UPB_ASSUME(UPB_PRIVATE(_upb_MiniTableField_GetRep)(f) ==
UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte));
UPB_ASSUME(upb_MiniTableField_IsScalar(f));
upb_MessageValue def;
def.msg_val = NULL;
return upb_Message_GetField(msg, f, def).msg_val;
}
UPB_API_INLINE upb_Array* upb_Message_GetMutableArray(
@ -507,7 +473,6 @@ UPB_INLINE struct upb_Map* _upb_Message_GetOrCreateMutableMap(
struct upb_Message* msg, const upb_MiniTableField* field, size_t key_size,
size_t val_size, upb_Arena* arena) {
UPB_PRIVATE(_upb_MiniTableField_CheckIsMap)(field);
UPB_PRIVATE(_upb_Message_AssertMapIsUntagged)(msg, field);
struct upb_Map* map = NULL;
struct upb_Map* default_map_value = NULL;
_upb_Message_GetNonExtensionField(msg, field, &default_map_value, &map);
@ -642,8 +607,12 @@ UPB_API_INLINE void upb_Message_SetBaseFieldInt64(struct upb_Message* msg,
UPB_API_INLINE void upb_Message_SetBaseFieldMessage(struct upb_Message* msg,
const upb_MiniTableField* f,
struct upb_Message* value) {
UPB_PRIVATE(_upb_Message_SetTaggedMessagePtr)
(msg, f, UPB_PRIVATE(_upb_TaggedMessagePtr_Pack)(value, false));
// TODO - Re-enable this assertion.
// UPB_ASSERT(value);
UPB_ASSUME(upb_MiniTableField_CType(f) == kUpb_CType_Message);
UPB_ASSUME(UPB_PRIVATE(_upb_MiniTableField_GetRep)(f) ==
UPB_SIZE(kUpb_FieldRep_4Byte, kUpb_FieldRep_8Byte));
upb_Message_SetBaseField(msg, f, &value);
}
UPB_API_INLINE void upb_Message_SetBaseFieldString(struct upb_Message* msg,

View file

@ -1,56 +0,0 @@
// 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
#ifndef UPB_MINI_TABLE_INTERNAL_TAGGED_PTR_H_
#define UPB_MINI_TABLE_INTERNAL_TAGGED_PTR_H_
#include <stdint.h>
#include "upb/message/internal/message.h"
// Must be last.
#include "upb/port/def.inc"
#ifdef __cplusplus
extern "C" {
#endif
// Internal-only because empty messages cannot be created by the user.
UPB_INLINE uintptr_t
UPB_PRIVATE(_upb_TaggedMessagePtr_Pack)(struct upb_Message* ptr, bool empty) {
UPB_ASSERT(((uintptr_t)ptr & 1) == 0);
return (uintptr_t)ptr | (empty ? 1 : 0);
}
UPB_API_INLINE bool upb_TaggedMessagePtr_IsEmpty(uintptr_t ptr) {
return ptr & 1;
}
UPB_INLINE struct upb_Message* UPB_PRIVATE(_upb_TaggedMessagePtr_GetMessage)(
uintptr_t ptr) {
return (struct upb_Message*)(ptr & ~(uintptr_t)1);
}
UPB_API_INLINE struct upb_Message* upb_TaggedMessagePtr_GetNonEmptyMessage(
uintptr_t ptr) {
UPB_ASSERT(!upb_TaggedMessagePtr_IsEmpty(ptr));
return UPB_PRIVATE(_upb_TaggedMessagePtr_GetMessage)(ptr);
}
UPB_INLINE struct upb_Message* UPB_PRIVATE(
_upb_TaggedMessagePtr_GetEmptyMessage)(uintptr_t ptr) {
UPB_ASSERT(upb_TaggedMessagePtr_IsEmpty(ptr));
return UPB_PRIVATE(_upb_TaggedMessagePtr_GetMessage)(ptr);
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#include "upb/port/undef.inc"
#endif /* UPB_MINI_TABLE_INTERNAL_TAGGED_PTR_H_ */

View file

@ -19,10 +19,8 @@
#include "upb/message/internal/array.h"
#include "upb/message/internal/extension.h"
#include "upb/message/internal/message.h"
#include "upb/message/internal/tagged_ptr.h"
#include "upb/message/map.h"
#include "upb/message/message.h"
#include "upb/message/tagged_ptr.h"
#include "upb/mini_table/extension.h"
#include "upb/mini_table/field.h"
#include "upb/mini_table/message.h"
@ -191,86 +189,6 @@ upb_FindUnknownRet upb_Message_FindUnknown(const upb_Message* msg,
return ret;
}
static upb_DecodeStatus upb_Message_PromoteOne(upb_TaggedMessagePtr* tagged,
const upb_MiniTable* mini_table,
int decode_options,
upb_Arena* arena) {
upb_Message* empty =
UPB_PRIVATE(_upb_TaggedMessagePtr_GetEmptyMessage)(*tagged);
upb_Message* promoted = upb_Message_New(mini_table, arena);
if (!promoted) return kUpb_DecodeStatus_OutOfMemory;
upb_StringView unknown_data;
uintptr_t iter = kUpb_Message_UnknownBegin;
while (upb_Message_NextUnknown(empty, &unknown_data, &iter)) {
upb_DecodeStatus status =
upb_Decode(unknown_data.data, unknown_data.size, promoted, mini_table,
NULL, decode_options, arena);
if (status != kUpb_DecodeStatus_Ok) {
return status;
}
}
*tagged = UPB_PRIVATE(_upb_TaggedMessagePtr_Pack)(promoted, false);
return kUpb_DecodeStatus_Ok;
}
upb_DecodeStatus upb_Message_PromoteMessage(upb_Message* parent,
const upb_MiniTable* mini_table,
const upb_MiniTableField* field,
int decode_options,
upb_Arena* arena,
upb_Message** promoted) {
UPB_ASSERT(!upb_Message_IsFrozen(parent));
const upb_MiniTable* sub_table =
upb_MiniTable_GetSubMessageTable(mini_table, field);
UPB_ASSERT(sub_table);
upb_TaggedMessagePtr tagged =
upb_Message_GetTaggedMessagePtr(parent, field, NULL);
upb_DecodeStatus ret =
upb_Message_PromoteOne(&tagged, sub_table, decode_options, arena);
if (ret == kUpb_DecodeStatus_Ok) {
*promoted = upb_TaggedMessagePtr_GetNonEmptyMessage(tagged);
upb_Message_SetMessage(parent, field, *promoted);
}
return ret;
}
upb_DecodeStatus upb_Array_PromoteMessages(upb_Array* arr,
const upb_MiniTable* mini_table,
int decode_options,
upb_Arena* arena) {
void** data = upb_Array_MutableDataPtr(arr);
size_t size = upb_Array_Size(arr);
for (size_t i = 0; i < size; i++) {
upb_TaggedMessagePtr tagged;
memcpy(&tagged, &data[i], sizeof(tagged));
if (!upb_TaggedMessagePtr_IsEmpty(tagged)) continue;
upb_DecodeStatus status =
upb_Message_PromoteOne(&tagged, mini_table, decode_options, arena);
if (status != kUpb_DecodeStatus_Ok) return status;
memcpy(&data[i], &tagged, sizeof(tagged));
}
return kUpb_DecodeStatus_Ok;
}
upb_DecodeStatus upb_Map_PromoteMessages(upb_Map* map,
const upb_MiniTable* mini_table,
int decode_options, upb_Arena* arena) {
size_t iter = kUpb_Map_Begin;
upb_MessageValue key, val;
while (upb_Map_Next(map, &key, &val, &iter)) {
if (!upb_TaggedMessagePtr_IsEmpty(val.tagged_msg_val)) continue;
upb_DecodeStatus status = upb_Message_PromoteOne(
&val.tagged_msg_val, mini_table, decode_options, arena);
if (status != kUpb_DecodeStatus_Ok) return status;
upb_Map_SetEntryValue(map, iter, val);
}
return kUpb_DecodeStatus_Ok;
}
////////////////////////////////////////////////////////////////////////////////
// OLD promotion functions, will be removed!
////////////////////////////////////////////////////////////////////////////////
// Warning: See TODO
upb_UnknownToMessageRet upb_MiniTable_PromoteUnknownToMessage(
upb_Message* msg, const upb_MiniTable* mini_table,

View file

@ -8,10 +8,16 @@
#ifndef UPB_MESSAGE_PROMOTE_H_
#define UPB_MESSAGE_PROMOTE_H_
#include <stddef.h>
#include <stdint.h>
#include "upb/mem/arena.h"
#include "upb/message/array.h"
#include "upb/message/map.h"
#include "upb/message/message.h"
#include "upb/message/value.h"
#include "upb/wire/decode.h"
#include "upb/mini_table/extension.h"
#include "upb/mini_table/field.h"
#include "upb/mini_table/message.h"
// Must be last.
#include "upb/port/def.inc"
@ -74,57 +80,10 @@ typedef struct {
upb_Message* message;
} upb_UnknownToMessageRet;
// Promotes an "empty" non-repeated message field in `parent` to a message of
// the correct type.
//
// Preconditions:
//
// 1. The message field must currently be in the "empty" state (this must have
// been previously verified by the caller by calling
// `upb_Message_GetTaggedMessagePtr()` and observing that the message is
// indeed empty).
//
// 2. This `field` must have previously been linked.
//
// If the promotion succeeds, `parent` will have its data for `field` replaced
// by the promoted message, which is also returned in `*promoted`. If the
// return value indicates an error status, `parent` and `promoted` are
// unchanged.
upb_DecodeStatus upb_Message_PromoteMessage(upb_Message* parent,
const upb_MiniTable* mini_table,
const upb_MiniTableField* field,
int decode_options,
upb_Arena* arena,
upb_Message** promoted);
// Promotes any "empty" messages in this array to a message of the correct type
// `mini_table`. This function should only be called for arrays of messages.
//
// If the return value indicates an error status, some but not all elements may
// have been promoted, but the array itself will not be corrupted.
upb_DecodeStatus upb_Array_PromoteMessages(upb_Array* arr,
const upb_MiniTable* mini_table,
int decode_options,
upb_Arena* arena);
// Promotes any "empty" entries in this map to a message of the correct type
// `mini_table`. This function should only be called for maps that have a
// message type as the map value.
//
// If the return value indicates an error status, some but not all elements may
// have been promoted, but the map itself will not be corrupted.
upb_DecodeStatus upb_Map_PromoteMessages(upb_Map* map,
const upb_MiniTable* mini_table,
int decode_options, upb_Arena* arena);
// Utility function for wrapper languages to get an error string from a
// upb_UnknownToMessageStatus.
const char* upb_FindUnknownStatus_String(upb_FindUnknown_Status status);
////////////////////////////////////////////////////////////////////////////////
// OLD promotion interfaces, will be removed!
////////////////////////////////////////////////////////////////////////////////
// Promotes unknown data inside message to a upb_Message parsing the unknown.
//
// The unknown data is removed from message after field value is set

View file

@ -33,7 +33,6 @@
#include "upb/message/internal/message.h"
#include "upb/message/map.h"
#include "upb/message/message.h"
#include "upb/message/tagged_ptr.h"
#include "upb/mini_descriptor/decode.h"
#include "upb/mini_descriptor/internal/encode.hpp"
#include "upb/mini_descriptor/internal/modifiers.h"
@ -307,425 +306,6 @@ TEST(GeneratedCode, Extensions) {
upb_Arena_Free(arena);
}
// Create a minitable to mimic ModelWithSubMessages with unlinked subs
// to lazily promote unknowns after parsing.
upb_MiniTable* CreateMiniTableWithEmptySubTables(upb_Arena* arena) {
upb::MtDataEncoder e;
e.StartMessage(0);
e.PutField(kUpb_FieldType_Int32, 4, 0);
e.PutField(kUpb_FieldType_Message, 5, 0);
e.PutField(kUpb_FieldType_Message, 6, kUpb_FieldModifier_IsRepeated);
upb_Status status;
upb_Status_Clear(&status);
upb_MiniTable* table =
upb_MiniTable_Build(e.data().data(), e.data().size(), arena, &status);
EXPECT_EQ(status.ok, true);
return table;
}
upb_MiniTable* CreateMapEntryMiniTable(upb_Arena* arena) {
upb::MtDataEncoder e;
e.EncodeMap(kUpb_FieldType_Int32, kUpb_FieldType_Message, 0, 0);
upb_Status status;
upb_Status_Clear(&status);
upb_MiniTable* table =
upb_MiniTable_Build(e.data().data(), e.data().size(), arena, &status);
EXPECT_EQ(status.ok, true);
return table;
}
// Create a minitable to mimic ModelWithMaps with unlinked subs
// to lazily promote unknowns after parsing.
upb_MiniTable* CreateMiniTableWithEmptySubTablesForMaps(upb_Arena* arena) {
upb::MtDataEncoder e;
e.StartMessage(0);
e.PutField(kUpb_FieldType_Int32, 1, 0);
e.PutField(kUpb_FieldType_Message, 3, kUpb_FieldModifier_IsRepeated);
e.PutField(kUpb_FieldType_Message, 5, kUpb_FieldModifier_IsRepeated);
upb_Status status;
upb_Status_Clear(&status);
upb_MiniTable* table =
upb_MiniTable_Build(e.data().data(), e.data().size(), arena, &status);
// Field 5 corresponds to ModelWithMaps.map_sm.
upb_MiniTableField* map_field = const_cast<upb_MiniTableField*>(
upb_MiniTable_FindFieldByNumber(table, 5));
EXPECT_NE(map_field, nullptr);
upb_MiniTable* sub_table = CreateMapEntryMiniTable(arena);
upb_MiniTable_SetSubMessage(table, map_field, sub_table);
EXPECT_EQ(status.ok, true);
return table;
}
void CheckReserialize(const upb_Message* msg, const upb_MiniTable* mini_table,
upb_Arena* arena, char* serialized,
size_t serialized_size) {
// We can safely encode the "empty" message. We expect to get the same bytes
// out as were parsed.
size_t reserialized_size;
char* reserialized;
upb_EncodeStatus encode_status =
upb_Encode(msg, mini_table, kUpb_EncodeOption_Deterministic, arena,
&reserialized, &reserialized_size);
EXPECT_EQ(encode_status, kUpb_EncodeStatus_Ok);
EXPECT_EQ(reserialized_size, serialized_size);
EXPECT_EQ(0, memcmp(reserialized, serialized, serialized_size));
// We should get the same result if we copy+reserialize.
upb_Message* clone = upb_Message_DeepClone(msg, mini_table, arena);
encode_status = upb_Encode(clone, mini_table, kUpb_EncodeOption_Deterministic,
arena, &reserialized, &reserialized_size);
EXPECT_EQ(encode_status, kUpb_EncodeStatus_Ok);
EXPECT_EQ(reserialized_size, serialized_size);
EXPECT_EQ(0, memcmp(reserialized, serialized, serialized_size));
}
TEST(GeneratedCode, PromoteUnknownMessage) {
upb::Arena arena;
upb_test_ModelWithSubMessages* input_msg =
upb_test_ModelWithSubMessages_new(arena.ptr());
upb_test_ModelWithExtensions* sub_message =
upb_test_ModelWithExtensions_new(arena.ptr());
upb_test_ModelWithSubMessages_set_id(input_msg, 11);
upb_test_ModelWithExtensions_set_random_int32(sub_message, 12);
upb_test_ModelWithSubMessages_set_optional_child(input_msg, sub_message);
size_t serialized_size;
char* serialized = upb_test_ModelWithSubMessages_serialize(
input_msg, arena.ptr(), &serialized_size);
upb_MiniTable* mini_table = CreateMiniTableWithEmptySubTables(arena.ptr());
upb_DecodeStatus decode_status;
// If we parse without allowing unlinked objects, the parse will fail.
// TODO: re-enable this test once the old method of tree shaking is
// removed
// upb_Message* fail_msg = _upb_Message_New(mini_table, arena.ptr());
// decode_status =
// upb_Decode(serialized, serialized_size, fail_msg, mini_table, nullptr,
// 0,
// arena.ptr());
// EXPECT_EQ(decode_status, kUpb_DecodeStatus_UnlinkedSubMessage);
// if we parse while allowing unlinked objects, the parse will succeed.
upb_Message* msg = _upb_Message_New(mini_table, arena.ptr());
decode_status =
upb_Decode(serialized, serialized_size, msg, mini_table, nullptr,
kUpb_DecodeOption_ExperimentalAllowUnlinked, arena.ptr());
EXPECT_EQ(decode_status, kUpb_DecodeStatus_Ok);
CheckReserialize(msg, mini_table, arena.ptr(), serialized, serialized_size);
// We can encode the "empty" message and get the same output bytes.
size_t reserialized_size;
char* reserialized;
upb_EncodeStatus encode_status = upb_Encode(
msg, mini_table, 0, arena.ptr(), &reserialized, &reserialized_size);
EXPECT_EQ(encode_status, kUpb_EncodeStatus_Ok);
EXPECT_EQ(reserialized_size, serialized_size);
EXPECT_EQ(0, memcmp(reserialized, serialized, serialized_size));
// Int32 field is present, as normal.
int32_t val = upb_Message_GetInt32(
msg, upb_MiniTable_FindFieldByNumber(mini_table, 4), 0);
EXPECT_EQ(val, 11);
// Unlinked sub-message is present, but getting the value returns NULL.
const upb_MiniTableField* submsg_field =
upb_MiniTable_FindFieldByNumber(mini_table, 5);
ASSERT_TRUE(submsg_field != nullptr);
EXPECT_TRUE(upb_Message_HasBaseField(msg, submsg_field));
upb_TaggedMessagePtr tagged =
upb_Message_GetTaggedMessagePtr(msg, submsg_field, nullptr);
EXPECT_TRUE(upb_TaggedMessagePtr_IsEmpty(tagged));
// Update mini table and promote unknown to a message.
EXPECT_TRUE(
upb_MiniTable_SetSubMessage(mini_table, (upb_MiniTableField*)submsg_field,
&upb_0test__ModelWithExtensions_msg_init));
const int decode_options =
upb_DecodeOptions_MaxDepth(0); // UPB_DECODE_ALIAS disabled.
upb_test_ModelWithExtensions* promoted;
upb_DecodeStatus promote_result =
upb_Message_PromoteMessage(msg, mini_table, submsg_field, decode_options,
arena.ptr(), (upb_Message**)&promoted);
EXPECT_EQ(promote_result, kUpb_DecodeStatus_Ok);
EXPECT_NE(nullptr, promoted);
EXPECT_EQ(UPB_UPCAST(promoted), upb_Message_GetMessage(msg, submsg_field));
EXPECT_EQ(upb_test_ModelWithExtensions_random_int32(promoted), 12);
}
// Tests a second parse that reuses an empty/unlinked message while the message
// is still unlinked.
TEST(GeneratedCode, ReparseUnlinked) {
upb::Arena arena;
upb_test_ModelWithSubMessages* input_msg =
upb_test_ModelWithSubMessages_new(arena.ptr());
upb_test_ModelWithExtensions* sub_message =
upb_test_ModelWithExtensions_new(arena.ptr());
upb_test_ModelWithSubMessages_set_id(input_msg, 11);
upb_test_ModelWithExtensions_add_repeated_int32(sub_message, 12, arena.ptr());
upb_test_ModelWithSubMessages_set_optional_child(input_msg, sub_message);
size_t serialized_size;
char* serialized = upb_test_ModelWithSubMessages_serialize(
input_msg, arena.ptr(), &serialized_size);
upb_MiniTable* mini_table = CreateMiniTableWithEmptySubTables(arena.ptr());
// Parse twice without linking the MiniTable.
upb_Message* msg = _upb_Message_New(mini_table, arena.ptr());
upb_DecodeStatus decode_status =
upb_Decode(serialized, serialized_size, msg, mini_table, nullptr,
kUpb_DecodeOption_ExperimentalAllowUnlinked, arena.ptr());
EXPECT_EQ(decode_status, kUpb_DecodeStatus_Ok);
decode_status =
upb_Decode(serialized, serialized_size, msg, mini_table, nullptr,
kUpb_DecodeOption_ExperimentalAllowUnlinked, arena.ptr());
EXPECT_EQ(decode_status, kUpb_DecodeStatus_Ok);
// Update mini table and promote unknown to a message.
const upb_MiniTableField* submsg_field =
upb_MiniTable_FindFieldByNumber(mini_table, 5);
EXPECT_TRUE(
upb_MiniTable_SetSubMessage(mini_table, (upb_MiniTableField*)submsg_field,
&upb_0test__ModelWithExtensions_msg_init));
const int decode_options =
upb_DecodeOptions_MaxDepth(0); // UPB_DECODE_ALIAS disabled.
upb_test_ModelWithExtensions* promoted;
upb_DecodeStatus promote_result =
upb_Message_PromoteMessage(msg, mini_table, submsg_field, decode_options,
arena.ptr(), (upb_Message**)&promoted);
EXPECT_EQ(promote_result, kUpb_DecodeStatus_Ok);
EXPECT_NE(nullptr, promoted);
EXPECT_EQ(UPB_UPCAST(promoted), upb_Message_GetMessage(msg, submsg_field));
// The repeated field should have two entries for the two parses.
size_t repeated_size;
const int32_t* entries =
upb_test_ModelWithExtensions_repeated_int32(promoted, &repeated_size);
EXPECT_EQ(repeated_size, 2);
EXPECT_EQ(entries[0], 12);
EXPECT_EQ(entries[1], 12);
}
// Tests a second parse that promotes a message within the parser because we are
// merging into an empty/unlinked message after the message has been linked.
TEST(GeneratedCode, PromoteInParser) {
upb::Arena arena;
upb_test_ModelWithSubMessages* input_msg =
upb_test_ModelWithSubMessages_new(arena.ptr());
upb_test_ModelWithExtensions* sub_message =
upb_test_ModelWithExtensions_new(arena.ptr());
upb_test_ModelWithSubMessages_set_id(input_msg, 11);
upb_test_ModelWithExtensions_add_repeated_int32(sub_message, 12, arena.ptr());
upb_test_ModelWithSubMessages_set_optional_child(input_msg, sub_message);
size_t serialized_size;
char* serialized = upb_test_ModelWithSubMessages_serialize(
input_msg, arena.ptr(), &serialized_size);
upb_MiniTable* mini_table = CreateMiniTableWithEmptySubTables(arena.ptr());
// Parse once without linking the MiniTable.
upb_Message* msg = _upb_Message_New(mini_table, arena.ptr());
upb_DecodeStatus decode_status =
upb_Decode(serialized, serialized_size, msg, mini_table, nullptr,
kUpb_DecodeOption_ExperimentalAllowUnlinked, arena.ptr());
EXPECT_EQ(decode_status, kUpb_DecodeStatus_Ok);
// Link the MiniTable.
const upb_MiniTableField* submsg_field =
upb_MiniTable_FindFieldByNumber(mini_table, 5);
EXPECT_TRUE(
upb_MiniTable_SetSubMessage(mini_table, (upb_MiniTableField*)submsg_field,
&upb_0test__ModelWithExtensions_msg_init));
// Parse again. This will promote the message. An explicit promote will not
// be required.
decode_status =
upb_Decode(serialized, serialized_size, msg, mini_table, nullptr,
kUpb_DecodeOption_ExperimentalAllowUnlinked, arena.ptr());
EXPECT_EQ(decode_status, kUpb_DecodeStatus_Ok);
upb_test_ModelWithExtensions* promoted =
(upb_test_ModelWithExtensions*)upb_Message_GetMessage(msg, submsg_field);
EXPECT_NE(nullptr, promoted);
EXPECT_EQ(UPB_UPCAST(promoted), upb_Message_GetMessage(msg, submsg_field));
// The repeated field should have two entries for the two parses.
size_t repeated_size;
const int32_t* entries =
upb_test_ModelWithExtensions_repeated_int32(promoted, &repeated_size);
EXPECT_EQ(repeated_size, 2);
EXPECT_EQ(entries[0], 12);
EXPECT_EQ(entries[1], 12);
}
TEST(GeneratedCode, PromoteUnknownRepeatedMessage) {
upb::Arena arena;
upb_test_ModelWithSubMessages* input_msg =
upb_test_ModelWithSubMessages_new(arena.ptr());
upb_test_ModelWithSubMessages_set_id(input_msg, 123);
// Add 2 repeated messages to input_msg.
upb_test_ModelWithExtensions* item =
upb_test_ModelWithSubMessages_add_items(input_msg, arena.ptr());
upb_test_ModelWithExtensions_set_random_int32(item, 5);
item = upb_test_ModelWithSubMessages_add_items(input_msg, arena.ptr());
upb_test_ModelWithExtensions_set_random_int32(item, 6);
size_t serialized_size;
char* serialized = upb_test_ModelWithSubMessages_serialize(
input_msg, arena.ptr(), &serialized_size);
upb_MiniTable* mini_table = CreateMiniTableWithEmptySubTables(arena.ptr());
upb_DecodeStatus decode_status;
// If we parse without allowing unlinked objects, the parse will fail.
// TODO: re-enable this test once the old method of tree shaking is
// removed
// upb_Message* fail_msg = _upb_Message_New(mini_table, arena.ptr());
// decode_status =
// upb_Decode(serialized, serialized_size, fail_msg, mini_table, nullptr,
// 0,
// arena.ptr());
// EXPECT_EQ(decode_status, kUpb_DecodeStatus_UnlinkedSubMessage);
// if we parse while allowing unlinked objects, the parse will succeed.
upb_Message* msg = _upb_Message_New(mini_table, arena.ptr());
decode_status =
upb_Decode(serialized, serialized_size, msg, mini_table, nullptr,
kUpb_DecodeOption_ExperimentalAllowUnlinked, arena.ptr());
CheckReserialize(msg, mini_table, arena.ptr(), serialized, serialized_size);
// Int32 field is present, as normal.
EXPECT_EQ(decode_status, kUpb_DecodeStatus_Ok);
int32_t val = upb_Message_GetInt32(
msg, upb_MiniTable_FindFieldByNumber(mini_table, 4), 0);
EXPECT_EQ(val, 123);
const upb_MiniTableField* repeated_field =
upb_MiniTable_FindFieldByNumber(mini_table, 6);
upb_Array* array = upb_Message_GetMutableArray(msg, repeated_field);
// Array length is 2 even though the messages are empty.
EXPECT_EQ(2, upb_Array_Size(array));
// Update mini table and promote unknown to a message.
EXPECT_TRUE(upb_MiniTable_SetSubMessage(
mini_table, (upb_MiniTableField*)repeated_field,
&upb_0test__ModelWithExtensions_msg_init));
const int decode_options =
upb_DecodeOptions_MaxDepth(0); // UPB_DECODE_ALIAS disabled.
upb_DecodeStatus promote_result =
upb_Array_PromoteMessages(array, &upb_0test__ModelWithExtensions_msg_init,
decode_options, arena.ptr());
EXPECT_EQ(promote_result, kUpb_DecodeStatus_Ok);
const upb_Message* promoted_message = upb_Array_Get(array, 0).msg_val;
EXPECT_EQ(upb_test_ModelWithExtensions_random_int32(
(upb_test_ModelWithExtensions*)promoted_message),
5);
promoted_message = upb_Array_Get(array, 1).msg_val;
EXPECT_EQ(upb_test_ModelWithExtensions_random_int32(
(upb_test_ModelWithExtensions*)promoted_message),
6);
}
TEST(GeneratedCode, PromoteUnknownToMap) {
upb::Arena arena;
upb_test_ModelWithMaps* input_msg = upb_test_ModelWithMaps_new(arena.ptr());
upb_test_ModelWithMaps_set_id(input_msg, 123);
upb_test_ModelWithExtensions* submsg0 =
upb_test_ModelWithExtensions_new(arena.ptr());
upb_test_ModelWithExtensions_set_random_int32(submsg0, 100);
upb_test_ModelWithExtensions* submsg1 =
upb_test_ModelWithExtensions_new(arena.ptr());
upb_test_ModelWithExtensions_set_random_int32(submsg1, 123);
upb_test_ModelWithExtensions* submsg2 =
upb_test_ModelWithExtensions_new(arena.ptr());
upb_test_ModelWithExtensions_set_random_int32(submsg2, 456);
// Add 3 map entries.
upb_test_ModelWithMaps_map_im_set(input_msg, 0, submsg0, arena.ptr());
upb_test_ModelWithMaps_map_im_set(input_msg, 111, submsg1, arena.ptr());
upb_test_ModelWithMaps_map_im_set(input_msg, 222, submsg2, arena.ptr());
size_t serialized_size;
char* serialized = upb_test_ModelWithMaps_serialize_ex(
input_msg, kUpb_EncodeOption_Deterministic, arena.ptr(),
&serialized_size);
upb_MiniTable* mini_table =
CreateMiniTableWithEmptySubTablesForMaps(arena.ptr());
// If we parse without allowing unlinked objects, the parse will fail.
upb_Message* fail_msg1 = _upb_Message_New(mini_table, arena.ptr());
upb_DecodeStatus decode_status =
upb_Decode(serialized, serialized_size, fail_msg1, mini_table, nullptr, 0,
arena.ptr());
EXPECT_EQ(decode_status, kUpb_DecodeStatus_UnlinkedSubMessage);
// if we parse while allowing unlinked objects, the parse will succeed.
upb_Message* msg = _upb_Message_New(mini_table, arena.ptr());
decode_status =
upb_Decode(serialized, serialized_size, msg, mini_table, nullptr,
kUpb_DecodeOption_ExperimentalAllowUnlinked, arena.ptr());
EXPECT_EQ(decode_status, kUpb_DecodeStatus_Ok);
CheckReserialize(msg, mini_table, arena.ptr(), serialized, serialized_size);
upb_MiniTableField* map_field = const_cast<upb_MiniTableField*>(
upb_MiniTable_FindFieldByNumber(mini_table, 5));
upb_Map* map = upb_Message_GetMutableMap(msg, map_field);
// Map size is 3 even though messages are unlinked.
EXPECT_EQ(3, upb_Map_Size(map));
// Update mini table and promote unknown to a message.
upb_MiniTable* entry = const_cast<upb_MiniTable*>(
upb_MiniTable_GetSubMessageTable(mini_table, map_field));
upb_MiniTableField* entry_value = const_cast<upb_MiniTableField*>(
upb_MiniTable_FindFieldByNumber(entry, 2));
upb_MiniTable_SetSubMessage(entry, entry_value,
&upb_0test__ModelWithExtensions_msg_init);
upb_DecodeStatus promote_result = upb_Map_PromoteMessages(
map, &upb_0test__ModelWithExtensions_msg_init, 0, arena.ptr());
EXPECT_EQ(promote_result, kUpb_DecodeStatus_Ok);
upb_MessageValue key;
upb_MessageValue val;
key.int32_val = 0;
EXPECT_TRUE(upb_Map_Get(map, key, &val));
EXPECT_EQ(100, upb_test_ModelWithExtensions_random_int32(
static_cast<const upb_test_ModelWithExtensions*>(
(void*)(val.msg_val))));
key.int32_val = 111;
EXPECT_TRUE(upb_Map_Get(map, key, &val));
EXPECT_EQ(123, upb_test_ModelWithExtensions_random_int32(
static_cast<const upb_test_ModelWithExtensions*>(
(void*)(val.msg_val))));
key.int32_val = 222;
EXPECT_TRUE(upb_Map_Get(map, key, &val));
EXPECT_EQ(456, upb_test_ModelWithExtensions_random_int32(
static_cast<const upb_test_ModelWithExtensions*>(
(void*)(val.msg_val))));
}
} // namespace
// OLD tests, to be removed!
namespace {
// Create a minitable to mimic ModelWithSubMessages with unlinked subs
// to lazily promote unknowns after parsing.
upb_MiniTable* CreateMiniTableWithEmptySubTablesOld(upb_Arena* arena) {

View file

@ -1,48 +0,0 @@
// 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
#ifndef UPB_MINI_TABLE_TAGGED_PTR_H_
#define UPB_MINI_TABLE_TAGGED_PTR_H_
#include <stdint.h>
#include "upb/message/internal/tagged_ptr.h"
#include "upb/message/message.h"
// Must be last.
#include "upb/port/def.inc"
// When a upb_Message* is stored in a message, array, or map, it is stored in a
// tagged form. If the tag bit is set, the referenced upb_Message is of type
// _kUpb_MiniTable_Empty (a sentinel message type with no fields) instead of
// that field's true message type. This forms the basis of what we call
// "dynamic tree shaking."
//
// See the documentation for kUpb_DecodeOption_ExperimentalAllowUnlinked for
// more information.
typedef uintptr_t upb_TaggedMessagePtr;
#ifdef __cplusplus
extern "C" {
#endif
// Users who enable unlinked sub-messages must use this to test whether a
// message is empty before accessing it. If a message is empty, it must be
// first promoted using the interfaces in message/promote.h.
UPB_API_INLINE bool upb_TaggedMessagePtr_IsEmpty(upb_TaggedMessagePtr ptr);
UPB_API_INLINE upb_Message* upb_TaggedMessagePtr_GetNonEmptyMessage(
upb_TaggedMessagePtr ptr);
#ifdef __cplusplus
} /* extern "C" */
#endif
#include "upb/port/undef.inc"
#endif /* UPB_MINI_TABLE_TAGGED_PTR_H_ */

View file

@ -733,7 +733,7 @@ TEST(MessageTest, MapFieldDeterministicEncoding) {
}
TEST(MessageTest, AdjacentAliasedUnknown) {
const upb_MiniTable* table = UPB_PRIVATE(_upb_MiniTable_Empty)();
const upb_MiniTable* table = &upb_0test__EmptyMessage_msg_init;
upb::Arena arena;
upb_Message* msg = upb_Message_New(table, arena.ptr());
char region[900];
@ -955,20 +955,17 @@ TEST(MessageTest, MessageTooBig) {
}
TEST(MessageTest, ArenaSpaceAllocatedAfterDecode) {
const upb_MiniTable* table = UPB_PRIVATE(_upb_MiniTable_Empty)();
upb::Arena arena(table->UPB_PRIVATE(size));
upb::Arena arena;
uintptr_t space_allocated_before =
upb_Arena_SpaceAllocated(arena.ptr(), nullptr);
upb_Message* msg = upb_Message_New(table, arena.ptr());
char region[300];
memset(region, 0, sizeof(region));
region[0] = 0x0A; // Tag number 1
region[1] = 0xA9;
region[2] = 0x02;
upb_DecodeStatus status =
upb_Decode(region, sizeof(region), msg, table, nullptr, 0, arena.ptr());
EXPECT_EQ(status, kUpb_DecodeStatus_Ok);
upb_test_EmptyMessage* msg =
upb_test_EmptyMessage_parse(region, sizeof(region), arena.ptr());
EXPECT_NE(msg, nullptr);
uintptr_t space_allocated_after =
upb_Arena_SpaceAllocated(arena.ptr(), nullptr);
EXPECT_GT(space_allocated_after, space_allocated_before + 297);

View file

@ -37,12 +37,6 @@ typedef union {
const struct upb_Message* msg_val;
upb_StringView str_val;
// EXPERIMENTAL: A tagged upb_Message*. Users must use this instead of
// msg_val if unlinked sub-messages may possibly be in use. See the
// documentation in kUpb_DecodeOption_ExperimentalAllowUnlinked for more
// information.
uintptr_t tagged_msg_val; // upb_TaggedMessagePtr
// For an extension field, we are essentially treating ext->data (a
// upb_MessageValue) as if it were a message with one field that lives at
// offset 0. This works because upb_MessageValue is precisely one value that

View file

@ -451,7 +451,7 @@ static void upb_MtDecoder_AllocateSubs(upb_MtDecoder* d,
ptrs_bytes ? upb_MtDecoder_CheckedMalloc(d, ptrs_bytes) : NULL;
uint32_t i = 0;
for (; i < sub_counts.submsg_count; i++) {
subs_ptrs[i] = UPB_PRIVATE(_upb_MiniTable_Empty)();
subs_ptrs[i] = NULL;
subs[i].UPB_PRIVATE(submsg) = &subs_ptrs[i];
}
if (sub_counts.subenum_count) {

View file

@ -227,7 +227,7 @@ TEST(MiniTableEnumTest, Enum) {
}
}
TEST_P(MiniTableTest, SubsInitializedToEmpty) {
TEST_P(MiniTableTest, SubsInitializedToNull) {
upb::Arena arena;
upb::MtDataEncoder e;
// Create mini table with 2 message fields.

View file

@ -58,7 +58,7 @@ bool upb_MiniTable_SetSubMessage(upb_MiniTable* table,
upb_MiniTableSubInternal* table_subs = (void*)table->UPB_PRIVATE(subs);
// TODO: Add this assert back once YouTube is updated to not call
// this function repeatedly.
// UPB_ASSERT(UPB_PRIVATE(_upb_MiniTable_IsEmpty)(table_sub->submsg));
// UPB_ASSERT(upb_MiniTable_GetSubMessageTable(table, field) == NULL);
memcpy((void*)table_subs[idx].UPB_PRIVATE(submsg), &sub, sizeof(void*));
return true;
}

View file

@ -14,23 +14,6 @@
// Must be last.
#include "upb/port/def.inc"
// A MiniTable for an empty message, used for unlinked sub-messages that are
// built via MiniDescriptors. Messages that use this MiniTable may possibly
// be linked later, in which case this MiniTable will be replaced with a real
// one. This pattern is known as "dynamic tree shaking", and it introduces
// complication because sub-messages may either be the "empty" type or the
// "real" type. A tagged bit indicates the difference.
const struct upb_MiniTable UPB_PRIVATE(_kUpb_MiniTable_Empty) = {
.UPB_PRIVATE(subs) = NULL,
.UPB_PRIVATE(fields) = NULL,
.UPB_PRIVATE(size) = sizeof(struct upb_Message),
.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,
};
// A MiniTable for a statically tree shaken message. Messages that use this
// MiniTable are guaranteed to remain unlinked; unlike the empty message, this
// MiniTable is never replaced, which greatly simplifies everything, because the

View file

@ -100,12 +100,6 @@ UPB_INLINE const struct upb_MiniTable* UPB_PRIVATE(
return mt;
}
UPB_INLINE const struct upb_MiniTable* UPB_PRIVATE(_upb_MiniTable_Empty)(void) {
extern const struct upb_MiniTable UPB_PRIVATE(_kUpb_MiniTable_Empty);
return &UPB_PRIVATE(_kUpb_MiniTable_Empty);
}
UPB_API_INLINE int upb_MiniTable_FieldCount(const struct upb_MiniTable* m) {
return m->UPB_ONLYBITS(field_count);
}
@ -158,13 +152,6 @@ const struct upb_MiniTableField* upb_MiniTable_FindFieldByNumber(
return NULL;
}
UPB_INLINE bool UPB_PRIVATE(_upb_MiniTable_IsEmpty)(
const struct upb_MiniTable* m) {
extern const struct upb_MiniTable UPB_PRIVATE(_kUpb_MiniTable_Empty);
return m == &UPB_PRIVATE(_kUpb_MiniTable_Empty);
}
UPB_API_INLINE const struct upb_MiniTableField* upb_MiniTable_GetFieldByIndex(
const struct upb_MiniTable* m, uint32_t i) {
return &m->UPB_ONLYBITS(fields)[i];
@ -188,9 +175,7 @@ UPB_API_INLINE const struct upb_MiniTable* upb_MiniTable_SubMessage(
UPB_API_INLINE const struct upb_MiniTable* upb_MiniTable_GetSubMessageTable(
const struct upb_MiniTable* m, const struct upb_MiniTableField* f) {
UPB_ASSUME(upb_MiniTableField_CType(f) == kUpb_CType_Message);
const struct upb_MiniTable* ret = upb_MiniTable_SubMessage(m, f);
UPB_ASSUME(ret);
return UPB_PRIVATE(_upb_MiniTable_IsEmpty)(ret) ? NULL : ret;
return upb_MiniTable_SubMessage(m, f);
}
UPB_API_INLINE bool upb_MiniTable_FieldIsLinked(

View file

@ -38,13 +38,8 @@ UPB_API_INLINE bool upb_MiniTable_IsMessageSet(const upb_MiniTable* m);
UPB_API_INLINE const upb_MiniTable* upb_MiniTable_GetSubMessageTable(
const upb_MiniTable* m, const upb_MiniTableField* f);
// Returns the MiniTable for a message field if it is a submessage, otherwise
// returns NULL.
//
// WARNING: if dynamic tree shaking is in use, the return value may be the
// "empty", zero-field placeholder message instead of the real message type.
// If the message is later linked, this function will begin returning the real
// message type.
// Returns the MiniTable for a message field if it is a submessage and the field
// is linked, otherwise returns NULL.
UPB_API_INLINE const upb_MiniTable* upb_MiniTable_SubMessage(
const upb_MiniTable* m, const upb_MiniTableField* f);

View file

@ -25,10 +25,8 @@
#include "upb/message/internal/map.h"
#include "upb/message/internal/map_entry.h"
#include "upb/message/internal/message.h"
#include "upb/message/internal/tagged_ptr.h"
#include "upb/message/map.h"
#include "upb/message/message.h"
#include "upb/message/tagged_ptr.h"
#include "upb/mini_table/enum.h"
#include "upb/mini_table/extension.h"
#include "upb/mini_table/extension_registry.h"
@ -249,62 +247,22 @@ static void _upb_Decoder_Munge(const upb_MiniTableField* field, wireval* val) {
static upb_Message* _upb_Decoder_NewSubMessage2(upb_Decoder* d,
const upb_MiniTable* subl,
const upb_MiniTableField* field,
upb_TaggedMessagePtr* target) {
upb_Message** target) {
UPB_ASSERT(subl);
upb_Message* msg = _upb_Message_New(subl, &d->arena);
if (!msg) _upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
// Extensions should not be unlinked. A message extension should not be
// registered until its sub-message type is available to be linked.
bool is_empty = UPB_PRIVATE(_upb_MiniTable_IsEmpty)(subl);
bool is_extension = field->UPB_PRIVATE(mode) & kUpb_LabelFlags_IsExtension;
UPB_ASSERT(!(is_empty && is_extension));
if (is_empty && !(d->options & kUpb_DecodeOption_ExperimentalAllowUnlinked)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_UnlinkedSubMessage);
}
upb_TaggedMessagePtr tagged =
UPB_PRIVATE(_upb_TaggedMessagePtr_Pack)(msg, is_empty);
memcpy(target, &tagged, sizeof(tagged));
*target = msg;
return msg;
}
static upb_Message* _upb_Decoder_NewSubMessage(
upb_Decoder* d, const upb_MiniTableSubInternal* subs,
const upb_MiniTableField* field, upb_TaggedMessagePtr* target) {
const upb_MiniTableField* field, upb_Message** target) {
const upb_MiniTable* subl = _upb_MiniTableSubs_MessageByField(subs, field);
return _upb_Decoder_NewSubMessage2(d, subl, field, target);
}
static upb_Message* _upb_Decoder_ReuseSubMessage(
upb_Decoder* d, const upb_MiniTableSubInternal* subs,
const upb_MiniTableField* field, upb_TaggedMessagePtr* target) {
upb_TaggedMessagePtr tagged = *target;
const upb_MiniTable* subl = _upb_MiniTableSubs_MessageByField(subs, field);
UPB_ASSERT(subl);
if (!upb_TaggedMessagePtr_IsEmpty(tagged) ||
UPB_PRIVATE(_upb_MiniTable_IsEmpty)(subl)) {
return UPB_PRIVATE(_upb_TaggedMessagePtr_GetMessage)(tagged);
}
// We found an empty message from a previous parse that was performed before
// this field was linked. But it is linked now, so we want to allocate a new
// message of the correct type and promote data into it before continuing.
upb_Message* existing =
UPB_PRIVATE(_upb_TaggedMessagePtr_GetEmptyMessage)(tagged);
upb_Message* promoted = _upb_Decoder_NewSubMessage(d, subs, field, target);
uintptr_t iter = kUpb_Message_UnknownBegin;
upb_StringView unknown;
while (upb_Message_NextUnknown(existing, &unknown, &iter)) {
upb_DecodeStatus status =
upb_Decode(unknown.data, unknown.size, promoted, subl, d->extreg,
d->options, &d->arena);
if (status != kUpb_DecodeStatus_Ok) _upb_Decoder_ErrorJmp(d, status);
}
return promoted;
}
static const char* _upb_Decoder_ReadString(upb_Decoder* d, const char* ptr,
int size, upb_StringView* str) {
const char* str_ptr = ptr;
@ -546,9 +504,9 @@ static const char* _upb_Decoder_DecodeToArray(
}
case kUpb_DecodeOp_SubMessage: {
/* Append submessage / group. */
upb_TaggedMessagePtr* target = UPB_PTR_AT(
upb_Array_MutableDataPtr(arr), arr->UPB_PRIVATE(size) * sizeof(void*),
upb_TaggedMessagePtr);
upb_Message** target =
UPB_PTR_AT(upb_Array_MutableDataPtr(arr),
arr->UPB_PRIVATE(size) * sizeof(void*), upb_Message*);
upb_Message* submsg = _upb_Decoder_NewSubMessage(d, subs, field, target);
arr->UPB_PRIVATE(size)++;
if (UPB_UNLIKELY(field->UPB_PRIVATE(descriptortype) ==
@ -665,10 +623,10 @@ static const char* _upb_Decoder_DecodeToMap(
entry->UPB_PRIVATE(fields)[1].UPB_PRIVATE(descriptortype) ==
kUpb_FieldType_Group) {
// Create proactively to handle the case where it doesn't appear.
upb_TaggedMessagePtr msg;
upb_Message* msg;
_upb_Decoder_NewSubMessage(d, entry->UPB_PRIVATE(subs),
&entry->UPB_PRIVATE(fields)[1], &msg);
ent.v.val = upb_value_uintptr(msg);
ent.v.val = upb_value_ptr(msg);
}
ptr = _upb_Decoder_DecodeSubMessage(d, ptr, &ent.message, subs, field,
@ -707,13 +665,9 @@ static const char* _upb_Decoder_DecodeToSubMessage(
// Store into message.
switch (op) {
case kUpb_DecodeOp_SubMessage: {
upb_TaggedMessagePtr* submsgp = mem;
upb_Message* submsg;
if (*submsgp) {
submsg = _upb_Decoder_ReuseSubMessage(d, subs, field, submsgp);
} else {
submsg = _upb_Decoder_NewSubMessage(d, subs, field, submsgp);
}
upb_Message** submsgp = mem;
upb_Message* submsg = *submsgp;
if (!submsg) submsg = _upb_Decoder_NewSubMessage(d, subs, field, submsgp);
if (UPB_UNLIKELY(type == kUpb_FieldType_Group)) {
ptr = _upb_Decoder_DecodeKnownGroup(d, ptr, submsg, subs, field);
} else {
@ -783,9 +737,10 @@ static void upb_Decoder_AddKnownMessageSetItem(
if (UPB_UNLIKELY(!ext)) {
_upb_Decoder_ErrorJmp(d, kUpb_DecodeStatus_OutOfMemory);
}
upb_Message** submsgp = (upb_Message**)&ext->data.msg_val;
upb_Message* submsg = _upb_Decoder_NewSubMessage2(
d, ext->ext->UPB_PRIVATE(sub).UPB_PRIVATE(submsg),
&ext->ext->UPB_PRIVATE(field), &ext->data.tagged_msg_val);
&ext->ext->UPB_PRIVATE(field), submsgp);
upb_DecodeStatus status = upb_Decode(
data, size, submsg, upb_MiniTableExtension_GetSubMessage(item_mt),
d->extreg, d->options, &d->arena);
@ -961,10 +916,7 @@ void _upb_Decoder_CheckUnlinked(upb_Decoder* d, const upb_MiniTable* mt,
if (field->UPB_PRIVATE(mode) & kUpb_LabelFlags_IsExtension) return;
const upb_MiniTable* mt_sub =
_upb_MiniTableSubs_MessageByField(mt->UPB_PRIVATE(subs), field);
if ((d->options & kUpb_DecodeOption_ExperimentalAllowUnlinked) ||
!UPB_PRIVATE(_upb_MiniTable_IsEmpty)(mt_sub)) {
return;
}
if (mt_sub != NULL) return; // Normal case, sub-message is linked.
#ifndef NDEBUG
const upb_MiniTableField* oneof = upb_MiniTable_GetOneof(mt, field);
if (oneof) {
@ -1452,8 +1404,6 @@ const char* upb_DecodeStatus_String(upb_DecodeStatus status) {
return "Exceeded upb_DecodeOptions_MaxDepth";
case kUpb_DecodeStatus_MissingRequired:
return "Missing required field";
case kUpb_DecodeStatus_UnlinkedSubMessage:
return "Unlinked sub-message field was present";
default:
return "Unknown decode status";
}

View file

@ -50,41 +50,6 @@ enum {
* post-parse validation step will always be necessary. */
kUpb_DecodeOption_CheckRequired = 2,
/* EXPERIMENTAL:
*
* If set, the parser will allow parsing of sub-message fields that were not
* previously linked using upb_MiniTable_SetSubMessage(). The data will be
* parsed into an internal "empty" message type that cannot be accessed
* directly, but can be later promoted into the true message type if the
* sub-message fields are linked at a later time.
*
* Users should set this option if they intend to perform dynamic tree shaking
* and promoting using the interfaces in message/promote.h. If this option is
* enabled, it is important that the resulting messages are only accessed by
* code that is aware of promotion rules:
*
* 1. Message pointers in upb_Message, upb_Array, and upb_Map are represented
* by a tagged pointer upb_TaggedMessagePointer. The tag indicates whether
* the message uses the internal "empty" type.
*
* 2. Any code *reading* these message pointers must test whether the "empty"
* tag bit is set, using the interfaces in mini_table/types.h. However
* writing of message pointers should always use plain upb_Message*, since
* users are not allowed to create "empty" messages.
*
* 3. It is always safe to test whether a field is present or test the array
* length; these interfaces will reflect that empty messages are present,
* even though their data cannot be accessed without promoting first.
*
* 4. If a message pointer is indeed tagged as empty, the message may not be
* accessed directly, only promoted through the interfaces in
* message/promote.h.
*
* 5. Tagged/empty messages may never be created by the user. They may only
* be created by the parser or the message-copying logic in message/copy.h.
*/
kUpb_DecodeOption_ExperimentalAllowUnlinked = 4,
/* EXPERIMENTAL:
*
* If set, decoding will enforce UTF-8 validation for string fields, even for
@ -127,13 +92,8 @@ typedef enum {
// kUpb_DecodeOption_CheckRequired failed (see above), but the parse otherwise
// succeeded.
kUpb_DecodeStatus_MissingRequired = 5,
// Unlinked sub-message field was present, but
// kUpb_DecodeOptions_ExperimentalAllowUnlinked was not specified in the list
// of options.
kUpb_DecodeStatus_UnlinkedSubMessage = 6,
} upb_DecodeStatus;
// LINT.ThenChange(//depot/google3/third_party/protobuf/rust/upb.rs:decode_status)
// LINT.ThenChange(//depot/google3/third_party/upb/rust/sys/wire/wire.rs:decode_status)
UPB_API upb_DecodeStatus upb_Decode(const char* buf, size_t size,
upb_Message* msg, const upb_MiniTable* mt,

View file

@ -30,10 +30,8 @@
#include "upb/message/internal/map_entry.h"
#include "upb/message/internal/map_sorter.h"
#include "upb/message/internal/message.h"
#include "upb/message/internal/tagged_ptr.h"
#include "upb/message/map.h"
#include "upb/message/message.h"
#include "upb/message/tagged_ptr.h"
#include "upb/mini_table/extension.h"
#include "upb/mini_table/field.h"
#include "upb/mini_table/internal/field.h"
@ -302,16 +300,6 @@ static char* encode_fixedarray(char* ptr, upb_encstate* e, const upb_Array* arr,
static char* encode_message(char* ptr, upb_encstate* e, const upb_Message* msg,
const upb_MiniTable* m, size_t* size);
static char* encode_TaggedMessagePtr(char* ptr, upb_encstate* e,
upb_TaggedMessagePtr tagged,
const upb_MiniTable* m, size_t* size) {
if (upb_TaggedMessagePtr_IsEmpty(tagged)) {
m = UPB_PRIVATE(_upb_MiniTable_Empty)();
}
return encode_message(
ptr, e, UPB_PRIVATE(_upb_TaggedMessagePtr_GetMessage)(tagged), m, size);
}
static char* encode_scalar(char* ptr, upb_encstate* e, const void* _field_mem,
const upb_MiniTableSubInternal* subs,
const upb_MiniTableField* f) {
@ -361,7 +349,7 @@ static char* encode_scalar(char* ptr, upb_encstate* e, const void* _field_mem,
}
case kUpb_FieldType_Group: {
size_t size;
upb_TaggedMessagePtr submsg = *(upb_TaggedMessagePtr*)field_mem;
upb_Message* submsg = *(upb_Message**)field_mem;
const upb_MiniTable* subm = _upb_Encoder_GetSubMiniTable(subs, f);
if (submsg == 0) {
return ptr;
@ -369,20 +357,20 @@ static char* encode_scalar(char* ptr, upb_encstate* e, const void* _field_mem,
if (--e->depth == 0) encode_err(e, kUpb_EncodeStatus_MaxDepthExceeded);
ptr = encode_tag(ptr, e, upb_MiniTableField_Number(f),
kUpb_WireType_EndGroup);
ptr = encode_TaggedMessagePtr(ptr, e, submsg, subm, &size);
ptr = encode_message(ptr, e, submsg, subm, &size);
wire_type = kUpb_WireType_StartGroup;
e->depth++;
break;
}
case kUpb_FieldType_Message: {
size_t size;
upb_TaggedMessagePtr submsg = *(upb_TaggedMessagePtr*)field_mem;
upb_Message* submsg = *(upb_Message**)field_mem;
const upb_MiniTable* subm = _upb_Encoder_GetSubMiniTable(subs, f);
if (submsg == 0) {
return ptr;
}
if (--e->depth == 0) encode_err(e, kUpb_EncodeStatus_MaxDepthExceeded);
ptr = encode_TaggedMessagePtr(ptr, e, submsg, subm, &size);
ptr = encode_message(ptr, e, submsg, subm, &size);
ptr = encode_length(ptr, e, size);
wire_type = kUpb_WireType_Delimited;
e->depth++;
@ -472,8 +460,8 @@ static char* encode_array(char* ptr, upb_encstate* e, const upb_Message* msg,
return ptr;
}
case kUpb_FieldType_Group: {
const upb_TaggedMessagePtr* start = upb_Array_DataPtr(arr);
const upb_TaggedMessagePtr* arr_ptr = start + upb_Array_Size(arr);
const upb_Message* const* start = upb_Array_DataPtr(arr);
const upb_Message* const* arr_ptr = start + upb_Array_Size(arr);
const upb_MiniTable* subm = _upb_Encoder_GetSubMiniTable(subs, f);
if (--e->depth == 0) encode_err(e, kUpb_EncodeStatus_MaxDepthExceeded);
do {
@ -481,7 +469,7 @@ static char* encode_array(char* ptr, upb_encstate* e, const upb_Message* msg,
arr_ptr--;
ptr = encode_tag(ptr, e, upb_MiniTableField_Number(f),
kUpb_WireType_EndGroup);
ptr = encode_TaggedMessagePtr(ptr, e, *arr_ptr, subm, &size);
ptr = encode_message(ptr, e, *arr_ptr, subm, &size);
ptr = encode_tag(ptr, e, upb_MiniTableField_Number(f),
kUpb_WireType_StartGroup);
} while (arr_ptr != start);
@ -489,14 +477,14 @@ static char* encode_array(char* ptr, upb_encstate* e, const upb_Message* msg,
return ptr;
}
case kUpb_FieldType_Message: {
const upb_TaggedMessagePtr* start = upb_Array_DataPtr(arr);
const upb_TaggedMessagePtr* arr_ptr = start + upb_Array_Size(arr);
const upb_Message* const* start = upb_Array_DataPtr(arr);
const upb_Message* const* arr_ptr = start + upb_Array_Size(arr);
const upb_MiniTable* subm = _upb_Encoder_GetSubMiniTable(subs, f);
if (--e->depth == 0) encode_err(e, kUpb_EncodeStatus_MaxDepthExceeded);
do {
size_t size;
arr_ptr--;
ptr = encode_TaggedMessagePtr(ptr, e, *arr_ptr, subm, &size);
ptr = encode_message(ptr, e, *arr_ptr, subm, &size);
ptr = encode_length(ptr, e, size);
ptr = encode_tag(ptr, e, upb_MiniTableField_Number(f),
kUpb_WireType_Delimited);