mirror of
https://github.com/protocolbuffers/protobuf
synced 2026-08-26 02:23:14 -04:00
Add a new upb_Message_Convert API that converts a upb message from one minitable type to another compatible one (e.g. they are both derived from the same proto definition via tree-shaking).
PiperOrigin-RevId: 922780263
This commit is contained in:
parent
1eee6a6b02
commit
0982838348
8 changed files with 2456 additions and 1 deletions
|
|
@ -178,6 +178,7 @@ upb/message/accessors.h
|
|||
upb/message/array.h
|
||||
upb/message/compare.h
|
||||
upb/message/compat.h
|
||||
upb/message/convert.h
|
||||
upb/message/copy.h
|
||||
upb/message/map.h
|
||||
upb/message/map_gencode_util.h
|
||||
|
|
|
|||
|
|
@ -244,6 +244,7 @@ cc_dist_library(
|
|||
"//upb:generated_code_support",
|
||||
"//upb/json",
|
||||
"//upb/message:compare",
|
||||
"//upb/message:convert",
|
||||
"//upb/message:copy",
|
||||
"//upb/mini_table:compat",
|
||||
"//upb/mini_table:debug_string",
|
||||
|
|
|
|||
|
|
@ -198,6 +198,63 @@ cc_library(
|
|||
],
|
||||
)
|
||||
|
||||
# Experimental API, subject to change.
|
||||
cc_library(
|
||||
name = "convert",
|
||||
srcs = [
|
||||
"convert.c",
|
||||
],
|
||||
hdrs = [
|
||||
"convert.h",
|
||||
],
|
||||
copts = UPB_DEFAULT_COPTS,
|
||||
visibility = [
|
||||
"//upb:__pkg__",
|
||||
"//upb:friends",
|
||||
],
|
||||
deps = [
|
||||
":compare",
|
||||
":internal",
|
||||
":message",
|
||||
"//upb/base",
|
||||
"//upb/mem",
|
||||
"//upb/mini_table",
|
||||
"//upb/mini_table:internal",
|
||||
"//upb/port",
|
||||
"//upb/wire",
|
||||
"//upb/wire:back_alloc",
|
||||
"//upb/wire:decoder",
|
||||
"//upb/wire:encoder",
|
||||
"//upb/wire:eps_copy_input_stream",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "convert_test",
|
||||
srcs = ["convert_test.cc"],
|
||||
deps = [
|
||||
":convert",
|
||||
":convert_test_upb_minitable_proto",
|
||||
":convert_test_upb_proto",
|
||||
":internal",
|
||||
":message",
|
||||
":message_test_upb_minitable_proto",
|
||||
":message_test_upb_proto",
|
||||
":promote",
|
||||
"//upb/base",
|
||||
"//upb/mem",
|
||||
"//upb/mini_table",
|
||||
"//upb/port",
|
||||
"//upb/test:test_messages_proto2_upb_proto",
|
||||
"//upb/test:test_messages_proto3_upb_minitable",
|
||||
"//upb/test:test_messages_proto3_upb_proto",
|
||||
"//upb/test:test_upb_proto",
|
||||
"//upb/wire",
|
||||
"@googletest//:gtest",
|
||||
"@googletest//:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "merge_test",
|
||||
srcs = ["merge_test.cc"],
|
||||
|
|
@ -416,6 +473,7 @@ cc_test(
|
|||
features = UPB_DEFAULT_FEATURES,
|
||||
deps = [
|
||||
":compare",
|
||||
":convert",
|
||||
":internal",
|
||||
":message",
|
||||
":message_test_upb_minitable_proto",
|
||||
|
|
@ -517,7 +575,10 @@ filegroup(
|
|||
[
|
||||
"**/*test.cc",
|
||||
],
|
||||
exclude = ["promote_test.cc"],
|
||||
exclude = [
|
||||
"promote_test.cc",
|
||||
"convert_fuzz_test.cc",
|
||||
],
|
||||
),
|
||||
visibility = ["//upb:__pkg__"],
|
||||
)
|
||||
|
|
@ -531,3 +592,21 @@ filegroup(
|
|||
),
|
||||
visibility = ["//upb:__pkg__"],
|
||||
)
|
||||
|
||||
proto_library(
|
||||
name = "convert_test_proto",
|
||||
testonly = 1,
|
||||
srcs = ["convert_test.proto"],
|
||||
)
|
||||
|
||||
upb_minitable_proto_library(
|
||||
name = "convert_test_upb_minitable_proto",
|
||||
testonly = 1,
|
||||
deps = [":convert_test_proto"],
|
||||
)
|
||||
|
||||
upb_c_proto_library(
|
||||
name = "convert_test_upb_proto",
|
||||
testonly = 1,
|
||||
deps = [":convert_test_proto"],
|
||||
)
|
||||
|
|
|
|||
646
upb/message/convert.c
Normal file
646
upb/message/convert.c
Normal file
|
|
@ -0,0 +1,646 @@
|
|||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2026 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
|
||||
|
||||
#include "upb/message/convert.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "upb/base/descriptor_constants.h"
|
||||
#include "upb/base/error_handler.h"
|
||||
#include "upb/base/string_view.h"
|
||||
#include "upb/mem/arena.h"
|
||||
#include "upb/message/accessors.h"
|
||||
#include "upb/message/array.h"
|
||||
#include "upb/message/compare.h"
|
||||
#include "upb/message/internal/accessors.h"
|
||||
#include "upb/message/internal/message.h"
|
||||
#include "upb/message/map.h"
|
||||
#include "upb/message/message.h"
|
||||
#include "upb/mini_table/enum.h"
|
||||
#include "upb/mini_table/extension.h"
|
||||
#include "upb/mini_table/extension_registry.h"
|
||||
#include "upb/mini_table/field.h"
|
||||
#include "upb/mini_table/internal/message.h"
|
||||
#include "upb/mini_table/message.h"
|
||||
#include "upb/wire/decode.h"
|
||||
#include "upb/wire/encode.h"
|
||||
#include "upb/wire/eps_copy_input_stream.h"
|
||||
#include "upb/wire/internal/back_alloc.h"
|
||||
#include "upb/wire/internal/decoder.h"
|
||||
#include "upb/wire/internal/encoder.h"
|
||||
|
||||
// Must be last.
|
||||
#include "upb/port/def.inc"
|
||||
|
||||
typedef struct {
|
||||
upb_Decoder decoder;
|
||||
upb_encstate encoder;
|
||||
upb_Arena* arena;
|
||||
upb_ErrorHandler err;
|
||||
} upb_Converter;
|
||||
|
||||
// Minitable compatibility type check on the field, but not the
|
||||
// submessage. Note: this check always succeeds for enums, whether the
|
||||
// enum is open or closed.
|
||||
UPB_INLINE bool _upb_MiniTableField_IsCompatible(
|
||||
const upb_MiniTableField* src_f, const upb_MiniTableField* dst_f) {
|
||||
return upb_MiniTableField_Type(src_f) == upb_MiniTableField_Type(dst_f) &&
|
||||
upb_MiniTableField_IsArray(src_f) ==
|
||||
upb_MiniTableField_IsArray(dst_f) &&
|
||||
upb_MiniTableField_IsMap(src_f) == upb_MiniTableField_IsMap(dst_f);
|
||||
}
|
||||
|
||||
UPB_INLINE bool _upb_MiniTableField_IsMapEntryCompatible(
|
||||
const upb_MiniTableField* src_f, const upb_MiniTableField* dst_f) {
|
||||
const upb_MiniTable* src_entry_mt = upb_MiniTable_MapEntrySubMessage(src_f);
|
||||
const upb_MiniTable* dst_entry_mt = upb_MiniTable_MapEntrySubMessage(dst_f);
|
||||
if (src_entry_mt == dst_entry_mt) return true;
|
||||
return _upb_MiniTableField_IsCompatible(upb_MiniTable_MapKey(src_entry_mt),
|
||||
upb_MiniTable_MapKey(dst_entry_mt)) &&
|
||||
_upb_MiniTableField_IsCompatible(upb_MiniTable_MapValue(src_entry_mt),
|
||||
upb_MiniTable_MapValue(dst_entry_mt));
|
||||
}
|
||||
|
||||
UPB_INLINE bool _upb_MiniTableField_IsExtensionCompatible(
|
||||
const upb_MiniTableField* src_f, const upb_MiniTableField* dst_f) {
|
||||
UPB_ASSERT(!upb_MiniTableField_IsMap(src_f));
|
||||
if (upb_MiniTableField_IsMap(dst_f)) return false;
|
||||
return upb_MiniTableField_Type(dst_f) == upb_MiniTableField_Type(src_f) &&
|
||||
upb_MiniTableField_IsArray(dst_f) == upb_MiniTableField_IsArray(src_f);
|
||||
}
|
||||
|
||||
static void upb_Message_SetFieldOrExtension(upb_Message* msg,
|
||||
const upb_MiniTableField* f,
|
||||
const upb_MiniTableExtension* ext,
|
||||
const upb_MessageValue* val,
|
||||
upb_Arena* arena) {
|
||||
if (ext != NULL) {
|
||||
upb_Message_SetExtension(msg, ext, val, arena);
|
||||
} else {
|
||||
upb_Message_SetBaseField(msg, f, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void upb_Message_EncodeFieldAsUnknown(
|
||||
upb_encstate* e, upb_Message* dst, const upb_Message* src,
|
||||
const upb_MiniTableField* src_field, int depth, upb_ErrorHandler* err) {
|
||||
size_t size;
|
||||
int encode_options = upb_Encode_LimitDepth(0, depth);
|
||||
char* buf = upb_BackAlloc_Init(&e->alloc, e->alloc.arena);
|
||||
UPB_PRIVATE(_upb_Encode_Field)(e, src, src_field, &buf, &size,
|
||||
encode_options);
|
||||
if (size > 0) {
|
||||
if (!UPB_PRIVATE(_upb_Message_AddUnknown)(dst, buf, size, e->alloc.arena,
|
||||
kUpb_AddUnknown_Alias)) {
|
||||
upb_ErrorHandler_ThrowError(err, kUpb_ErrorCode_OutOfMemory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void upb_Message_EncodeExtensionAsUnknown(
|
||||
upb_encstate* e, upb_Message* dst, const upb_MiniTable* dst_mt,
|
||||
const upb_MiniTableExtension* ext, upb_MessageValue val, int depth,
|
||||
upb_ErrorHandler* err) {
|
||||
size_t size;
|
||||
int encode_options = upb_Encode_LimitDepth(0, depth);
|
||||
bool is_message_set = upb_MiniTable_IsMessageSet(dst_mt);
|
||||
char* buf = upb_BackAlloc_Init(&e->alloc, e->alloc.arena);
|
||||
UPB_PRIVATE(_upb_Encode_Extension)(e, ext, val, is_message_set, &buf, &size,
|
||||
encode_options);
|
||||
if (size > 0) {
|
||||
if (!UPB_PRIVATE(_upb_Message_AddUnknown)(dst, buf, size, e->alloc.arena,
|
||||
kUpb_AddUnknown_Alias)) {
|
||||
upb_ErrorHandler_ThrowError(err, kUpb_ErrorCode_OutOfMemory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void upb_Message_ConvertInternal(upb_Converter* c, upb_Message* dst,
|
||||
const upb_Message* src,
|
||||
const upb_MiniTable* dst_mt,
|
||||
const upb_MiniTable* src_mt,
|
||||
const upb_ExtensionRegistry* extreg,
|
||||
int depth);
|
||||
|
||||
static void upb_Array_DeepConvert(
|
||||
upb_Converter* c, upb_Array* dst, const upb_Array* src,
|
||||
const upb_MiniTable* dst_sub_mt, const upb_MiniTable* src_sub_mt,
|
||||
const upb_MiniTableField* dst_f, upb_Message* dst_msg,
|
||||
const upb_ExtensionRegistry* extreg, int depth) {
|
||||
size_t size = upb_Array_Size(src);
|
||||
if (!upb_Array_Resize(dst, size, c->arena)) {
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
}
|
||||
size_t dst_i = 0;
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
upb_MessageValue src_val = upb_Array_Get(src, i);
|
||||
if (upb_MiniTableField_IsClosedEnum(dst_f)) {
|
||||
const upb_MiniTableEnum* dst_e = upb_MiniTable_GetSubEnumTable(dst_f);
|
||||
if (upb_MiniTableEnum_CheckValue(dst_e, src_val.int32_val)) {
|
||||
upb_MessageValue dst_val;
|
||||
dst_val.int32_val = src_val.int32_val;
|
||||
upb_Array_Set(dst, dst_i++, dst_val);
|
||||
} else if (!_upb_Encoder_AddEnumValueToUnknown(
|
||||
dst_msg, dst_f, src_val.int32_val, c->arena)) {
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
}
|
||||
} else {
|
||||
const upb_Message* src_msg = src_val.msg_val;
|
||||
upb_Message* dst_sub = upb_Message_New(dst_sub_mt, c->arena);
|
||||
if (!dst_sub) {
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
}
|
||||
upb_Message_ConvertInternal(c, dst_sub, src_msg, dst_sub_mt, src_sub_mt,
|
||||
extreg, depth);
|
||||
upb_MessageValue dst_val;
|
||||
dst_val.msg_val = dst_sub;
|
||||
upb_Array_Set(dst, dst_i++, dst_val);
|
||||
}
|
||||
}
|
||||
if (dst_i != size) {
|
||||
upb_Array_Resize(dst, dst_i, c->arena);
|
||||
}
|
||||
}
|
||||
|
||||
static bool upb_Message_ConvertArrayField(upb_Converter* c, upb_Message* dst,
|
||||
const upb_Message* src,
|
||||
const upb_MiniTableField* dst_f,
|
||||
const upb_MiniTableField* src_f,
|
||||
const upb_ExtensionRegistry* extreg,
|
||||
int depth) {
|
||||
const upb_Array* src_arr = upb_Message_GetArray(src, src_f);
|
||||
if (!src_arr) return true;
|
||||
|
||||
const upb_MiniTable* dst_sub_mt = upb_MiniTable_SubMessage(dst_f);
|
||||
const upb_MiniTable* src_sub_mt = upb_MiniTable_SubMessage(src_f);
|
||||
|
||||
if (dst_sub_mt != src_sub_mt || upb_MiniTableField_IsClosedEnum(dst_f)) {
|
||||
upb_Array* dst_arr =
|
||||
upb_Array_New(c->arena, upb_MiniTableField_CType(dst_f));
|
||||
if (!dst_arr)
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
upb_Array_DeepConvert(c, dst_arr, src_arr, dst_sub_mt, src_sub_mt, dst_f,
|
||||
dst, extreg, depth);
|
||||
upb_Message_SetBaseField(dst, dst_f, &dst_arr);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void upb_Map_DeepConvert(
|
||||
upb_Converter* c, upb_Map* dst, const upb_Map* src,
|
||||
const upb_MiniTable* dst_entry_mt, const upb_MiniTable* src_entry_mt,
|
||||
const upb_MiniTableField* dst_map_f, upb_Message* dst_msg,
|
||||
const upb_ExtensionRegistry* extreg, int depth) {
|
||||
const upb_MiniTableField* dst_val_f = upb_MiniTable_MapValue(dst_entry_mt);
|
||||
const upb_MiniTable* dst_val_mt = upb_MiniTable_SubMessage(dst_val_f);
|
||||
const upb_MiniTableField* src_val_f = upb_MiniTable_MapValue(src_entry_mt);
|
||||
const upb_MiniTable* src_val_mt = upb_MiniTable_SubMessage(src_val_f);
|
||||
|
||||
size_t iter = kUpb_Map_Begin;
|
||||
upb_MessageValue key, src_val;
|
||||
while (upb_Map_Next(src, &key, &src_val, &iter)) {
|
||||
if (dst_val_mt && src_val_mt) {
|
||||
const upb_Message* src_msg = src_val.msg_val;
|
||||
upb_Message* dst_sub = upb_Message_New(dst_val_mt, c->arena);
|
||||
if (!dst_sub) {
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
}
|
||||
upb_Message_ConvertInternal(c, dst_sub, src_msg, dst_val_mt, src_val_mt,
|
||||
extreg, depth);
|
||||
upb_MessageValue dst_val;
|
||||
dst_val.msg_val = dst_sub;
|
||||
if (!upb_Map_Set(dst, key, dst_val, c->arena)) {
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
}
|
||||
} else {
|
||||
// Scalar value.
|
||||
if (upb_MiniTableField_IsClosedEnum(dst_val_f)) {
|
||||
const upb_MiniTableEnum* dst_e =
|
||||
upb_MiniTable_GetSubEnumTable(dst_val_f);
|
||||
if (upb_MiniTableEnum_CheckValue(dst_e, src_val.int32_val)) {
|
||||
if (!upb_Map_Set(dst, key, src_val, c->arena)) {
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
}
|
||||
} else {
|
||||
upb_Message* ent_msg = upb_Message_New(src_entry_mt, c->arena);
|
||||
if (!ent_msg) {
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
}
|
||||
upb_Message_SetBaseField(ent_msg, upb_MiniTable_MapKey(src_entry_mt),
|
||||
&key);
|
||||
upb_Message_SetBaseField(
|
||||
ent_msg, upb_MiniTable_MapValue(src_entry_mt), &src_val);
|
||||
_upb_Encoder_AddMapEntryUnknown(dst_msg, dst_map_f, ent_msg,
|
||||
src_entry_mt, c->arena);
|
||||
}
|
||||
} else {
|
||||
if (!upb_Map_Set(dst, key, src_val, c->arena)) {
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool upb_Message_ConvertMapField(upb_Converter* c, upb_Message* dst,
|
||||
const upb_Message* src,
|
||||
const upb_MiniTableField* dst_f,
|
||||
const upb_MiniTableField* src_f,
|
||||
const upb_ExtensionRegistry* extreg,
|
||||
int depth) {
|
||||
const upb_Map* src_map = upb_Message_GetMap(src, src_f);
|
||||
if (!src_map) return true;
|
||||
|
||||
const upb_MiniTable* dst_entry_mt = upb_MiniTable_MapEntrySubMessage(dst_f);
|
||||
const upb_MiniTable* src_entry_mt = upb_MiniTable_MapEntrySubMessage(src_f);
|
||||
|
||||
if (dst_entry_mt != src_entry_mt || upb_MiniTableField_IsClosedEnum(dst_f)) {
|
||||
const upb_MiniTableField* dst_val_f = upb_MiniTable_MapValue(dst_entry_mt);
|
||||
upb_Map* dst_map = upb_Map_New(
|
||||
c->arena, upb_MiniTableField_CType(upb_MiniTable_MapKey(dst_entry_mt)),
|
||||
upb_MiniTableField_CType(dst_val_f));
|
||||
if (!dst_map) {
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
}
|
||||
upb_Map_DeepConvert(c, dst_map, src_map, dst_entry_mt, src_entry_mt, dst_f,
|
||||
dst, extreg, depth);
|
||||
upb_Message_SetBaseField(dst, dst_f, &dst_map);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void upb_Message_ConvertField(upb_Converter* c, upb_Message* dst,
|
||||
const upb_Message* src,
|
||||
const upb_MiniTableField* dst_f,
|
||||
const upb_MiniTableField* src_f,
|
||||
const upb_ExtensionRegistry* extreg,
|
||||
int depth) {
|
||||
if (upb_MiniTableField_HasPresence(src_f)) {
|
||||
if (!upb_Message_HasBaseField(src, src_f)) return;
|
||||
} else if (upb_MiniTableField_IsScalar(src_f)) {
|
||||
// For proto3 implicit scalar fields, we only need to copy if the source
|
||||
// field is set.
|
||||
const void* src_data = UPB_PRIVATE(_upb_Message_DataPtr)(src, src_f);
|
||||
if (UPB_PRIVATE(_upb_MiniTableField_DataIsZero)(src_f, src_data)) return;
|
||||
}
|
||||
|
||||
if (upb_MiniTableField_CType(dst_f) == kUpb_CType_Message) {
|
||||
if (upb_MiniTableField_IsScalar(dst_f)) {
|
||||
const upb_Message* src_sub = upb_Message_GetMessage(src, src_f);
|
||||
if (!src_sub) return;
|
||||
|
||||
const upb_MiniTable* dst_sub_mt = upb_MiniTable_SubMessage(dst_f);
|
||||
const upb_MiniTable* src_sub_mt = upb_MiniTable_SubMessage(src_f);
|
||||
|
||||
if (dst_sub_mt == src_sub_mt) {
|
||||
upb_Message_SetMessage(dst, dst_f, (upb_Message*)src_sub);
|
||||
return;
|
||||
}
|
||||
|
||||
upb_Message* dst_sub =
|
||||
upb_Message_GetOrCreateMutableMessage(dst, dst_f, c->arena);
|
||||
if (!dst_sub)
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
upb_Message_ConvertInternal(c, dst_sub, src_sub, dst_sub_mt, src_sub_mt,
|
||||
extreg, depth);
|
||||
return;
|
||||
} else if (upb_MiniTableField_IsArray(dst_f)) {
|
||||
if (upb_Message_ConvertArrayField(c, dst, src, dst_f, src_f, extreg,
|
||||
depth)) {
|
||||
return;
|
||||
}
|
||||
} else if (upb_MiniTableField_IsMap(dst_f)) {
|
||||
if (UPB_UNLIKELY(
|
||||
!_upb_MiniTableField_IsMapEntryCompatible(src_f, dst_f))) {
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_Malformed);
|
||||
}
|
||||
if (upb_Message_ConvertMapField(c, dst, src, dst_f, src_f, extreg,
|
||||
depth)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (upb_MiniTableField_IsClosedEnum(dst_f)) {
|
||||
if (upb_MiniTableField_IsArray(dst_f)) {
|
||||
if (upb_Message_ConvertArrayField(c, dst, src, dst_f, src_f, extreg,
|
||||
depth)) {
|
||||
return;
|
||||
}
|
||||
} else if (upb_MiniTableField_IsMap(dst_f)) {
|
||||
if (upb_Message_ConvertMapField(c, dst, src, dst_f, src_f, extreg,
|
||||
depth)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
int32_t val;
|
||||
memcpy(&val, UPB_PRIVATE(_upb_Message_DataPtr)(src, src_f), 4);
|
||||
const upb_MiniTableEnum* dst_e = upb_MiniTable_GetSubEnumTable(dst_f);
|
||||
if (!upb_MiniTableEnum_CheckValue(dst_e, val)) {
|
||||
if (!_upb_Encoder_AddEnumValueToUnknown(dst, dst_f, val, c->arena)) {
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UPB_PRIVATE(_upb_MiniTableField_DataCopy)
|
||||
(dst_f, UPB_PRIVATE(_upb_Message_MutableDataPtr)(dst, dst_f),
|
||||
UPB_PRIVATE(_upb_Message_DataPtr)(src, src_f));
|
||||
|
||||
if (upb_MiniTableField_HasPresence(dst_f)) {
|
||||
UPB_PRIVATE(_upb_Message_SetPresence)(dst, dst_f);
|
||||
}
|
||||
}
|
||||
|
||||
static void upb_Message_ConvertExtensions(upb_Converter* c, upb_Message* dst,
|
||||
const upb_Message* src,
|
||||
const upb_MiniTable* dst_mt,
|
||||
const upb_ExtensionRegistry* extreg,
|
||||
int depth) {
|
||||
const upb_MiniTableExtension* ext;
|
||||
upb_MessageValue val;
|
||||
uintptr_t iter = kUpb_Message_ExtensionBegin;
|
||||
while (upb_Message_NextExtension(src, &ext, &val, &iter)) {
|
||||
const upb_MiniTableField* dst_f = upb_MiniTable_FindFieldByNumber(
|
||||
dst_mt, upb_MiniTableExtension_Number(ext));
|
||||
const upb_MiniTableExtension* dst_ext = NULL;
|
||||
if (!dst_f) {
|
||||
// Source extension not found in the destination schema. Check the
|
||||
// extension registry.
|
||||
if (extreg != NULL) {
|
||||
dst_ext = upb_ExtensionRegistry_Lookup(
|
||||
extreg, dst_mt, upb_MiniTableExtension_Number(ext));
|
||||
if (dst_ext) {
|
||||
dst_f = upb_MiniTableExtension_ToField(dst_ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dst_f) {
|
||||
const upb_MiniTableField* src_f = upb_MiniTableExtension_ToField(ext);
|
||||
|
||||
UPB_ASSERT(!upb_MiniTableField_IsMap(src_f));
|
||||
if (UPB_UNLIKELY(
|
||||
!_upb_MiniTableField_IsExtensionCompatible(src_f, dst_f))) {
|
||||
// Return an error due to type mismatch.
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_Malformed);
|
||||
}
|
||||
|
||||
if (upb_MiniTableField_CType(dst_f) == kUpb_CType_Message) {
|
||||
const upb_MiniTable* dst_sub_mt = upb_MiniTable_SubMessage(dst_f);
|
||||
const upb_MiniTable* src_sub_mt = upb_MiniTable_SubMessage(src_f);
|
||||
|
||||
if (upb_MiniTableField_IsArray(dst_f)) {
|
||||
if (dst_sub_mt != src_sub_mt) {
|
||||
// Array of messages, and the sub message types differ. Perform
|
||||
// conversion.
|
||||
upb_Array* dst_arr = upb_Array_New(c->arena, kUpb_CType_Message);
|
||||
if (!dst_arr)
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
upb_Array_DeepConvert(c, dst_arr, val.array_val, dst_sub_mt,
|
||||
src_sub_mt, dst_f, dst, extreg, depth);
|
||||
upb_MessageValue valid_val;
|
||||
valid_val.array_val = dst_arr;
|
||||
upb_Message_SetFieldOrExtension(dst, dst_f, dst_ext, &valid_val,
|
||||
c->arena);
|
||||
} else {
|
||||
// Array of messages, and the sub message types are the same.
|
||||
// Shallow copy.
|
||||
upb_Message_SetFieldOrExtension(dst, dst_f, dst_ext, &val,
|
||||
c->arena);
|
||||
}
|
||||
} else if (dst_sub_mt == src_sub_mt) {
|
||||
// Scalar message, and the message types are the same.
|
||||
// Shallow copy.
|
||||
upb_Message_SetFieldOrExtension(dst, dst_f, dst_ext, &val, c->arena);
|
||||
} else {
|
||||
// Scalar message, and the message types differ. Perform conversion.
|
||||
upb_Message* dst_sub = upb_Message_New(dst_sub_mt, c->arena);
|
||||
if (!dst_sub)
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
|
||||
upb_Message_ConvertInternal(c, dst_sub, val.msg_val, dst_sub_mt,
|
||||
src_sub_mt, extreg, depth);
|
||||
|
||||
upb_MessageValue valid_val;
|
||||
valid_val.msg_val = dst_sub;
|
||||
upb_Message_SetFieldOrExtension(dst, dst_f, dst_ext, &valid_val,
|
||||
c->arena);
|
||||
}
|
||||
} else {
|
||||
// Scalar non-message type.
|
||||
if (upb_MiniTableField_IsClosedEnum(dst_f)) {
|
||||
if (upb_MiniTableField_IsArray(dst_f)) {
|
||||
upb_Array* dst_arr = upb_Array_New(c->arena, kUpb_CType_Int32);
|
||||
if (!dst_arr)
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
upb_Array_DeepConvert(c, dst_arr, val.array_val, NULL, NULL, dst_f,
|
||||
dst, extreg, depth);
|
||||
upb_MessageValue valid_val;
|
||||
valid_val.array_val = dst_arr;
|
||||
upb_Message_SetFieldOrExtension(dst, dst_f, dst_ext, &valid_val,
|
||||
c->arena);
|
||||
continue;
|
||||
} else {
|
||||
const upb_MiniTableEnum* dst_e =
|
||||
dst_ext ? upb_MiniTableExtension_GetSubEnum(dst_ext)
|
||||
: upb_MiniTable_GetSubEnumTable(dst_f);
|
||||
if (!upb_MiniTableEnum_CheckValue(dst_e, val.int32_val)) {
|
||||
if (!_upb_Encoder_AddEnumValueToUnknown(dst, dst_f, val.int32_val,
|
||||
c->arena)) {
|
||||
upb_ErrorHandler_ThrowError(&c->err,
|
||||
kUpb_ErrorCode_OutOfMemory);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
upb_Message_SetFieldOrExtension(dst, dst_f, dst_ext, &val, c->arena);
|
||||
}
|
||||
} else {
|
||||
// Extension not found in the destination schema.
|
||||
if (dst_mt->UPB_PRIVATE(ext) == kUpb_ExtMode_NonExtendable) {
|
||||
// Destination message does not support extensions. Encode the extension
|
||||
// as an unknown field in the destination message.
|
||||
upb_Message_EncodeExtensionAsUnknown(&c->encoder, dst, dst_mt, ext, val,
|
||||
depth, &c->err);
|
||||
} else if (!upb_Message_SetExtension(dst, ext, &val, c->arena)) {
|
||||
// Destination message supports extensions. Since this extension is not
|
||||
// known in the destination schema, we simply carry over the source
|
||||
// extension.
|
||||
// TODO - b/510055656: to handle the non-canonical extension properly.
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_OutOfMemory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void upb_Message_ConvertInternal(upb_Converter* c, upb_Message* dst,
|
||||
const upb_Message* src,
|
||||
const upb_MiniTable* dst_mt,
|
||||
const upb_MiniTable* src_mt,
|
||||
const upb_ExtensionRegistry* extreg,
|
||||
int depth) {
|
||||
UPB_ASSERT(dst != NULL);
|
||||
if (--depth == 0) {
|
||||
// TODO:b/494593478 - Add a new error code for max depth exceeded.
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_Malformed);
|
||||
}
|
||||
|
||||
const upb_MiniTableField* dst_f = NULL;
|
||||
const upb_MiniTableField* dst_first = NULL;
|
||||
const upb_MiniTableField* src_f = NULL;
|
||||
const upb_MiniTableField* src_first = NULL;
|
||||
|
||||
if (upb_MiniTable_FieldCount(dst_mt) > 0) {
|
||||
dst_first = upb_MiniTable_GetFieldByIndex(dst_mt, 0);
|
||||
dst_f = dst_first + upb_MiniTable_FieldCount(dst_mt);
|
||||
}
|
||||
if (upb_MiniTable_FieldCount(src_mt) > 0) {
|
||||
src_first = upb_MiniTable_GetFieldByIndex(src_mt, 0);
|
||||
src_f = src_first + upb_MiniTable_FieldCount(src_mt);
|
||||
}
|
||||
|
||||
// Convert fields in descending order of field number.
|
||||
while (dst_f != dst_first || src_f != src_first) {
|
||||
uint32_t dst_nr =
|
||||
dst_f != dst_first ? upb_MiniTableField_Number(dst_f - 1) : 0;
|
||||
uint32_t src_nr =
|
||||
src_f != src_first ? upb_MiniTableField_Number(src_f - 1) : 0;
|
||||
|
||||
if (dst_nr == src_nr) {
|
||||
const upb_MiniTableField* dst_next = dst_f - 1;
|
||||
const upb_MiniTableField* src_next = src_f - 1;
|
||||
|
||||
if (UPB_UNLIKELY(!_upb_MiniTableField_IsCompatible(src_next, dst_next))) {
|
||||
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_Malformed);
|
||||
}
|
||||
upb_Message_ConvertField(c, dst, src, dst_next, src_next, extreg, depth);
|
||||
dst_f--;
|
||||
src_f--;
|
||||
} else if (dst_nr > src_nr) {
|
||||
dst_f--;
|
||||
} else {
|
||||
const upb_MiniTableField* src_next = src_f - 1;
|
||||
upb_Message_EncodeFieldAsUnknown(&c->encoder, dst, src, src_next, depth,
|
||||
&c->err);
|
||||
src_f--;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert extensions.
|
||||
if (src_mt->UPB_PRIVATE(ext) != kUpb_ExtMode_NonExtendable) {
|
||||
upb_Message_ConvertExtensions(c, dst, src, dst_mt, extreg, depth);
|
||||
}
|
||||
|
||||
// Convert unknown fields.
|
||||
upb_StringView data;
|
||||
size_t iter = kUpb_Message_UnknownBegin;
|
||||
while (upb_Message_NextUnknown(src, &data, &iter)) {
|
||||
int decode_options =
|
||||
upb_Decode_LimitDepth(kUpb_DecodeOption_AliasString, depth);
|
||||
|
||||
// Reuse d. Reset input stream.
|
||||
const char* ptr = data.data;
|
||||
upb_Decoder* d = &c->decoder;
|
||||
upb_EpsCopyInputStream_InitWithErrorHandler(&d->input, &ptr, data.size,
|
||||
d->err);
|
||||
upb_Decoder_Reset(d, decode_options, dst);
|
||||
_upb_Decoder_DecodeMessage(d, ptr, dst, dst_mt);
|
||||
UPB_ASSERT(d->end_group == DECODE_NOGROUP);
|
||||
}
|
||||
}
|
||||
|
||||
static bool upb_Message_DoConvert(upb_Converter* c, upb_Message* dst,
|
||||
const upb_Message* src,
|
||||
const upb_MiniTable* dst_mt,
|
||||
const upb_MiniTable* src_mt,
|
||||
const upb_ExtensionRegistry* extreg) {
|
||||
if (UPB_SETJMP(c->err.buf) == 0) {
|
||||
upb_Message_ConvertInternal(c, dst, src, dst_mt, src_mt, extreg, 100);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const upb_Message* upb_Message_Convert(const upb_Message* src,
|
||||
const upb_MiniTable* src_mt,
|
||||
const upb_MiniTable* dst_mt,
|
||||
const upb_ExtensionRegistry* extreg,
|
||||
upb_Arena* arena) {
|
||||
if (dst_mt == src_mt && extreg == NULL) return src;
|
||||
|
||||
upb_Message* dst = upb_Message_New(dst_mt, arena);
|
||||
if (!dst) return NULL;
|
||||
|
||||
upb_Converter c;
|
||||
upb_ErrorHandler_Init(&c.err);
|
||||
|
||||
// Initialize the decoder.
|
||||
// Initialize decoder once, performing SwapIn.
|
||||
// We use a NULL buffer initially, effectively a dummy init to set up the
|
||||
// arena and error handler. Note: we pass &c.err.
|
||||
upb_Decoder_Init(&c.decoder, NULL, 0, extreg, 0, arena, &c.err, NULL, 0);
|
||||
|
||||
// Initialize the encoder.
|
||||
UPB_PRIVATE(_upb_encstate_init)(&c.encoder, &c.err.buf, &c.decoder.arena);
|
||||
|
||||
c.arena = &c.decoder.arena;
|
||||
|
||||
if (!upb_Message_DoConvert(&c, dst, src, dst_mt, src_mt, extreg)) {
|
||||
dst = NULL;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (dst) {
|
||||
char *wire_buf, *wire_buf2;
|
||||
size_t wire_size, wire_size2;
|
||||
upb_Arena* tmp_arena = upb_Arena_New();
|
||||
|
||||
// Compare the encoded/decoded round-trip of the original message to the
|
||||
// encoded/decoded round-trip of the converted message.
|
||||
//
|
||||
// We cannot compare the messages directly using
|
||||
// UPB_ASSERT(upb_Message_IsEqual(dst, decoded_msg, dst_mt, 0)), as we
|
||||
// essentially handle the non-canonical extensions differently in the
|
||||
// conversion. See b/510055656 for details.
|
||||
// Instead, we compare the encoded/decoded round-trip of the original
|
||||
// message to the encoded/decoded round-trip of the converted message.
|
||||
// They should be identical, as the non-canonical extensions will be encoded
|
||||
// as unknown fields.
|
||||
|
||||
// Round-trip 1: encode/decode original message `src`
|
||||
upb_EncodeStatus encode_status =
|
||||
upb_Encode(src, src_mt, 0, tmp_arena, &wire_buf, &wire_size);
|
||||
UPB_ASSERT(encode_status == kUpb_EncodeStatus_Ok);
|
||||
upb_Message* decoded_msg = upb_Message_New(dst_mt, tmp_arena);
|
||||
upb_DecodeStatus decode_status = upb_Decode(
|
||||
wire_buf, wire_size, decoded_msg, dst_mt, extreg, 0, tmp_arena);
|
||||
UPB_ASSERT(decode_status == kUpb_DecodeStatus_Ok);
|
||||
|
||||
// Round-trip 2: encode/decode converted message `dst`
|
||||
upb_EncodeStatus encode_status2 =
|
||||
upb_Encode(dst, dst_mt, 0, tmp_arena, &wire_buf2, &wire_size2);
|
||||
UPB_ASSERT(encode_status2 == kUpb_EncodeStatus_Ok);
|
||||
upb_Message* decoded_msg2 = upb_Message_New(dst_mt, tmp_arena);
|
||||
upb_DecodeStatus decode_status2 = upb_Decode(
|
||||
wire_buf2, wire_size2, decoded_msg2, dst_mt, extreg, 0, tmp_arena);
|
||||
UPB_ASSERT(decode_status2 == kUpb_DecodeStatus_Ok);
|
||||
|
||||
// Compare both decoded messages.
|
||||
UPB_ASSERT(upb_Message_IsEqual(decoded_msg, decoded_msg2, dst_mt, 0));
|
||||
upb_Arena_Free(tmp_arena);
|
||||
}
|
||||
#endif
|
||||
|
||||
upb_Decoder_Destroy(&c.decoder, arena);
|
||||
UPB_PRIVATE(_upb_encstate_destroy)(&c.encoder);
|
||||
return dst;
|
||||
}
|
||||
59
upb/message/convert.h
Normal file
59
upb/message/convert.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// 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_MESSAGE_CONVERT_H_
|
||||
#define UPB_MESSAGE_CONVERT_H_
|
||||
|
||||
#include "upb/mem/arena.h"
|
||||
#include "upb/message/message.h"
|
||||
#include "upb/mini_table/extension_registry.h"
|
||||
#include "upb/mini_table/message.h"
|
||||
|
||||
// Must be last.
|
||||
#include "upb/port/def.inc"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Converts a message between two different `upb_MiniTable` types. The source
|
||||
// and destination `upb_MiniTable`s must be compatible, e.g. they are both
|
||||
// derived from the same proto definition (e.g., via tree-shaking) or subsets of
|
||||
// some message proto.
|
||||
//
|
||||
// It is equivalent to encoding the source message and then decoding it
|
||||
// using the destination `upb_MiniTable`, but is generally faster and uses less
|
||||
// memory.
|
||||
//
|
||||
// If the destination message, or any of its submessages, uses the same
|
||||
// minitable as the matching part of the source (determined by pointer
|
||||
// identity), the destination will alias the source's memory (shallow copy)
|
||||
// instead of performing a deep copy. Strings and unknown fields are aliased
|
||||
// from the input message.
|
||||
//
|
||||
// Fields present in the source but not the destination will be encoded and
|
||||
// added to the destination's unknown fields (or extensions, if the extension
|
||||
// registry allows it).
|
||||
//
|
||||
// Returns a new message on success, or NULL on failure. This function may
|
||||
// return NULL even for valid inputs, if it encounters a case it does not
|
||||
// support. If the caller wishes to handle all cases, they should detect NULL
|
||||
// and fallback to serializing the source message and then decoding it using
|
||||
// the destination `upb_MiniTable`.
|
||||
const upb_Message* upb_Message_Convert(const upb_Message* src,
|
||||
const upb_MiniTable* src_mt,
|
||||
const upb_MiniTable* dst_mt,
|
||||
const upb_ExtensionRegistry* extreg,
|
||||
upb_Arena* arena);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#include "upb/port/undef.inc"
|
||||
|
||||
#endif // UPB_MESSAGE_CONVERT_H_
|
||||
343
upb/message/convert_fuzz_test.cc
Normal file
343
upb/message/convert_fuzz_test.cc
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "testing/fuzzing/fuzztest.h"
|
||||
#include "upb/mem/arena.h"
|
||||
#include "upb/message/compare.h"
|
||||
#include "upb/message/convert.h"
|
||||
#include "upb/message/message.h"
|
||||
#include "upb/mini_table/extension_registry.h"
|
||||
#include "upb/mini_table/field.h"
|
||||
#include "upb/mini_table/internal/field.h"
|
||||
#include "upb/mini_table/internal/message.h"
|
||||
#include "upb/mini_table/internal/sub.h"
|
||||
#include "upb/mini_table/message.h"
|
||||
#include "upb/test/fuzz_util.h"
|
||||
#include "upb/wire/decode.h"
|
||||
#include "upb/wire/encode.h"
|
||||
|
||||
// Must be last.
|
||||
#include "upb/port/def.inc"
|
||||
|
||||
namespace upb {
|
||||
namespace {
|
||||
|
||||
const upb_MiniTable* SubsetMiniTable(const upb_MiniTable* src, uint64_t mask,
|
||||
upb_Arena* arena);
|
||||
|
||||
const upb_MiniTable* SubsetMiniTable(const upb_MiniTable* src, uint64_t mask,
|
||||
upb_Arena* arena) {
|
||||
std::vector<upb_MiniTableField> new_fields;
|
||||
std::vector<upb_MiniTableSub> new_subs;
|
||||
|
||||
int field_count = upb_MiniTable_FieldCount(src);
|
||||
for (int i = 0; i < field_count; ++i) {
|
||||
const upb_MiniTableField* f = upb_MiniTable_GetFieldByIndex(src, i);
|
||||
if (mask & (1ULL << (i % 64))) {
|
||||
upb_MiniTableField new_f = *f;
|
||||
if (f->UPB_PRIVATE(submsg_ofs) != kUpb_NoSub) {
|
||||
new_subs.push_back(
|
||||
*UPB_PTR_AT(f, f->UPB_PRIVATE(submsg_ofs) * kUpb_SubmsgOffsetBytes,
|
||||
const upb_MiniTableSub));
|
||||
}
|
||||
new_fields.push_back(new_f);
|
||||
}
|
||||
}
|
||||
|
||||
size_t mt_size = sizeof(upb_MiniTable);
|
||||
size_t fields_bytes = new_fields.size() * sizeof(upb_MiniTableField);
|
||||
size_t subs_bytes = new_subs.size() * sizeof(upb_MiniTableSub);
|
||||
|
||||
size_t mt_padded_size = UPB_ALIGN_UP(mt_size, 8);
|
||||
size_t fields_padded_size = UPB_ALIGN_UP(fields_bytes, 8);
|
||||
size_t total_size = mt_padded_size + fields_padded_size + subs_bytes;
|
||||
|
||||
upb_MiniTable* new_mt = (upb_MiniTable*)upb_Arena_Malloc(arena, total_size);
|
||||
upb_MiniTableField* fields_ptr =
|
||||
UPB_PTR_AT(new_mt, mt_padded_size, upb_MiniTableField);
|
||||
upb_MiniTableSub* subs_ptr =
|
||||
UPB_PTR_AT(fields_ptr, fields_padded_size, upb_MiniTableSub);
|
||||
|
||||
std::memcpy(new_mt, src, mt_size);
|
||||
if (!new_fields.empty()) {
|
||||
std::memcpy(fields_ptr, new_fields.data(), fields_bytes);
|
||||
}
|
||||
if (!new_subs.empty()) {
|
||||
std::memcpy(subs_ptr, new_subs.data(), subs_bytes);
|
||||
}
|
||||
|
||||
int sub_idx = 0;
|
||||
for (size_t i = 0; i < new_fields.size(); ++i) {
|
||||
upb_MiniTableField* f = &fields_ptr[i];
|
||||
if (f->UPB_PRIVATE(submsg_ofs) != kUpb_NoSub) {
|
||||
uintptr_t f_addr = (uintptr_t)f;
|
||||
uintptr_t subs_addr = (uintptr_t)&subs_ptr[sub_idx++];
|
||||
|
||||
size_t diff = subs_addr - f_addr;
|
||||
f->UPB_PRIVATE(submsg_ofs) = (uint16_t)(diff / kUpb_SubmsgOffsetBytes);
|
||||
}
|
||||
}
|
||||
|
||||
new_mt->UPB_ONLYBITS(fields) = fields_ptr;
|
||||
new_mt->UPB_ONLYBITS(field_count) = (uint16_t)new_fields.size();
|
||||
new_mt->UPB_PRIVATE(dense_below) = 0;
|
||||
new_mt->UPB_PRIVATE(table_mask) = -1;
|
||||
|
||||
return new_mt;
|
||||
}
|
||||
|
||||
void ConvertFuzz(const upb::fuzz::MiniTableFuzzInput& input, uint64_t mask1,
|
||||
uint64_t mask2, std::string proto_payload,
|
||||
uint32_t decode_options, uint32_t encode_options) {
|
||||
upb_Arena* arena = upb_Arena_New();
|
||||
|
||||
upb_ExtensionRegistry* exts;
|
||||
const upb_MiniTable* original_mt =
|
||||
upb::fuzz::BuildMiniTable(input, &exts, arena);
|
||||
if (!original_mt) {
|
||||
upb_Arena_Free(arena);
|
||||
return;
|
||||
}
|
||||
|
||||
const upb_MiniTable* src_mt = SubsetMiniTable(original_mt, mask1, arena);
|
||||
const upb_MiniTable* dst_mt = SubsetMiniTable(original_mt, mask2, arena);
|
||||
|
||||
decode_options = upb_Decode_LimitDepth(decode_options, 80);
|
||||
encode_options = upb_Encode_LimitDepth(encode_options, 80);
|
||||
|
||||
// We don't want to skip unknown fields or check required fields, as these
|
||||
// will cause the fuzz test to fail or exit early in ways that aren't
|
||||
// interesting.
|
||||
encode_options &=
|
||||
~(kUpb_EncodeOption_SkipUnknown | kUpb_EncodeOption_CheckRequired);
|
||||
|
||||
upb_Message* msg_orig = upb_Message_New(original_mt, arena);
|
||||
upb_DecodeStatus status =
|
||||
upb_Decode(proto_payload.data(), proto_payload.size(), msg_orig,
|
||||
original_mt, exts, decode_options, arena);
|
||||
|
||||
if (status != kUpb_DecodeStatus_Ok) {
|
||||
upb_Arena_Free(arena);
|
||||
return;
|
||||
}
|
||||
|
||||
upb_Message* msg_src = upb_Message_New(src_mt, arena);
|
||||
status = upb_Decode(proto_payload.data(), proto_payload.size(), msg_src,
|
||||
src_mt, exts, decode_options, arena);
|
||||
|
||||
if (status != kUpb_DecodeStatus_Ok) {
|
||||
upb_Arena_Free(arena);
|
||||
return;
|
||||
}
|
||||
|
||||
const upb_Message* msg_dst =
|
||||
upb_Message_Convert(msg_src, src_mt, dst_mt, nullptr, arena);
|
||||
if (!msg_dst) {
|
||||
upb_Arena_Free(arena);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t size;
|
||||
char* bytes;
|
||||
upb_EncodeStatus enc_status =
|
||||
upb_Encode(msg_dst, dst_mt, encode_options, arena, &bytes, &size);
|
||||
|
||||
if (enc_status != kUpb_EncodeStatus_Ok) {
|
||||
upb_Arena_Free(arena);
|
||||
return;
|
||||
}
|
||||
|
||||
upb_Message* msg_final = upb_Message_New(original_mt, arena);
|
||||
status = upb_Decode(bytes, size, msg_final, original_mt, exts, decode_options,
|
||||
arena);
|
||||
|
||||
if (status != kUpb_DecodeStatus_Ok) {
|
||||
upb_Arena_Free(arena);
|
||||
return;
|
||||
}
|
||||
|
||||
bool equal = upb_Message_IsEqual(msg_final, msg_orig, original_mt,
|
||||
kUpb_CompareOption_IncludeUnknownFields);
|
||||
if (!equal) {
|
||||
abort();
|
||||
}
|
||||
|
||||
upb_Arena_Free(arena);
|
||||
}
|
||||
|
||||
FUZZ_TEST(ConvertFuzz, ConvertFuzz);
|
||||
|
||||
TEST(ConvertFuzz, Convert_IdenticalMinitables_ShallowCopy) {
|
||||
ConvertFuzz(
|
||||
upb::fuzz::MiniTableFuzzInput{
|
||||
{"\331", ""}, {"\257"}, "\351\210", {2147483645}},
|
||||
4867317803475403639ULL, 4867317803475403639ULL, "@B", 3220282077ULL,
|
||||
4189253947ULL);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz, Convert_DifferentSubsets_DroppedFields_Complex) {
|
||||
ConvertFuzz(
|
||||
upb::fuzz::MiniTableFuzzInput{
|
||||
{"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||
"", ""},
|
||||
{"\306", "", "\331", ""},
|
||||
"\314Q\345\322\024\321\017\021\016\016\016",
|
||||
{1, 1, 0, 4294967295, 4294967295,
|
||||
1, 4294967295, 4294967295, 4294967295, 4294967295,
|
||||
4294967295, 4294967295, 4294967295, 4294967295, 4294967295,
|
||||
1, 3135226496, 3135226496, 3135226496, 0,
|
||||
3708883408}},
|
||||
14162464183153632842ULL, 2939036597827910531ULL,
|
||||
"=\032\253\342K\221MQ\nj\n\304\361\364\304", 1620786928, 2077918455);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz, Convert_DifferentSubsets_DroppedFields_Simple) {
|
||||
ConvertFuzz(upb::fuzz::MiniTableFuzzInput{{"", "", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", ""},
|
||||
{""},
|
||||
"",
|
||||
{4294967295, 4294967295, 0}},
|
||||
4453300303650383170ULL, 13549612121351043620ULL, "]]k\031\320",
|
||||
1804506072, 3299701550);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz,
|
||||
Convert_DifferentSubsets_DroppedFields_Regression_89982ef4cfe52331) {
|
||||
ConvertFuzz(
|
||||
upb::fuzz::MiniTableFuzzInput{
|
||||
{"", "DD", "z", "", "", "\200"}, {"\031", "\031", "\031"}, "]]", {}},
|
||||
7595264386850118164ULL, 1703605996941841092ULL, "\300D\315\316\372D",
|
||||
3067512230, 2237854510);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz, ManualUnknownFieldRepro) {
|
||||
ConvertFuzz(upb::fuzz::MiniTableFuzzInput{{"I", "a"}}, ~0ULL, 0ULL,
|
||||
"\010\000", 0, 0);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz, Regression_FuzzerFinding_UnknownFields) {
|
||||
ConvertFuzz(
|
||||
upb::fuzz::MiniTableFuzzInput{
|
||||
{"", "", ""}, {"", "\211", "", "{", "\232", "2"}, "", {4294967295}},
|
||||
5205171011975038739ULL, 18014293590545137538ULL, "E\261k\333\334",
|
||||
590856108, 53173774);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz, RepeatedFieldConfusion) {
|
||||
ConvertFuzz(upb::fuzz::MiniTableFuzzInput{{"\t", "\352", "R", "$\214\214\214",
|
||||
"\004", "$F", "\244", "$\374P"},
|
||||
{""},
|
||||
"{GGG",
|
||||
{4294967295}},
|
||||
3117542645911838959, 9223372036854775807, "", 2147221503, 16);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz, ConvertFuzzRegression) {
|
||||
ConvertFuzz(
|
||||
upb::fuzz::MiniTableFuzzInput{{""}, {"\325\242"}, "", {2147483647}},
|
||||
18446744073709551615u, 10490713252739160816u, " u", 2, 4294967231);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz, ConvertFuzzRegression_Crash_ConvertInternal_2026_04_07) {
|
||||
ConvertFuzz(
|
||||
upb::fuzz::MiniTableFuzzInput{
|
||||
{""},
|
||||
{"", ">>", "", ""},
|
||||
"\357\357\357\357\357*"
|
||||
"\357\357\357\357\357\357\357\357\357\357\357\357\357\357\357\357\357"
|
||||
"\357\225\225\225\357\357\357\357\357\357\357\357\357\357\357\357\357"
|
||||
"\357\357\357\357\357\357\357\357\357",
|
||||
{4294967295, 4294967295, 4294967295, 4294967295, 4294967295,
|
||||
4294967295, 4294967295, 4294967295, 1}},
|
||||
12411566311597166710ULL, 8799022017001952335ULL, "%%%%%%%%%%%%%%%%%ZZ%",
|
||||
2815879784ULL, 3243839465ULL);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz, Convert_Fuzz_Crash_Regression) {
|
||||
ConvertFuzz(
|
||||
upb::fuzz::MiniTableFuzzInput{
|
||||
{"", "", ""}, {"", ""}, "\207\254", {4033684729, 4294967294}},
|
||||
7437277671727957814ULL, 9800553862402446025ULL,
|
||||
"\220Y\203\225\225\366\337\337\004", 3062476814ULL, 4041248646ULL);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz, ConvertFuzzRegression2) {
|
||||
ConvertFuzz(upb::fuzz::MiniTableFuzzInput{{"", ""}, {""}, "", {1903095162}},
|
||||
8810965477460052779, 18446744073709551615u, "\030\030", 114210880,
|
||||
8190);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz, ConvertFuzzRegression3) {
|
||||
ConvertFuzz(upb::fuzz::MiniTableFuzzInput{{""}, {}, "\373", {}}, 1,
|
||||
9223372036854775807, "uz\010\006\006", 4294967290, 2147483647);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz, ConvertFuzzRegression4) {
|
||||
ConvertFuzz(
|
||||
upb::fuzz::MiniTableFuzzInput{
|
||||
{"$", "C", ""},
|
||||
{"r"},
|
||||
"\270N\270[",
|
||||
{3621539714, 3621539714, 3621539714, 3621539714, 3621539714,
|
||||
3621539714, 3621539714, 3621539714, 3621539714, 3621539714,
|
||||
3621539714, 3621539714, 3621539714, 3621539714, 3621539714,
|
||||
3621539714, 3621539714, 3621539714, 3621539714, 3621539714,
|
||||
3621539714, 3621539714, 3621539714, 3621539714, 3621539714,
|
||||
3621539714, 3621539714, 3621539714, 3621539714, 3621539714,
|
||||
3621539714, 3621539714, 3621539714, 3621539714, 3621539714,
|
||||
3621539714, 3621539714, 3621539714, 3621539714, 3621539714,
|
||||
3621539714, 3621539714, 3621539714, 3621539714}},
|
||||
13429813313754688149ULL, 6819704094736752274ULL, "\255\032\262\337))",
|
||||
1611082566, 466142871);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz, ConvertFuzzRegression5) {
|
||||
ConvertFuzz(upb::fuzz::MiniTableFuzzInput{{""}, {"\362", "y"}, "\252", {}},
|
||||
8728619288361297649ULL, 13997967148621545864ULL,
|
||||
"%\311\311\311\303", 3383556142, 3604956055);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz, ConvertFuzzRegression6) {
|
||||
ConvertFuzz(
|
||||
upb::fuzz::MiniTableFuzzInput{
|
||||
{"", "", "\341", "\341", "\341", "$", "", "", "\022", "\022", "", "",
|
||||
""},
|
||||
{"\374", "\204"},
|
||||
"n\251PXq",
|
||||
{1, 1, 1, 1, 1,
|
||||
1, 1, 1413050348, 1, 1,
|
||||
1, 1, 1, 0, 0,
|
||||
1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 4,
|
||||
4294967295, 4294967295, 4294967295, 4294967295, 4294967295,
|
||||
4294967295, 4294967295, 4294967295, 1, 1,
|
||||
1, 1, 1, 1, 1,
|
||||
1, 1, 1, 3028396088, 1,
|
||||
1, 1, 1, 1, 4294967295,
|
||||
1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 0,
|
||||
1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1}},
|
||||
304186569338573724ULL, 6555165345294515829ULL, "8W", 3970284081,
|
||||
1611359599);
|
||||
}
|
||||
|
||||
TEST(ConvertFuzz, ConvertFuzzEncodeRegression) {
|
||||
ConvertFuzz(
|
||||
upb::fuzz::MiniTableFuzzInput{{"$$$$$$$$$$$$$$$$$$", "", "", "", "", ""},
|
||||
{"", ""},
|
||||
"D",
|
||||
{2462394141, 2462394141, 2462394145}},
|
||||
555217012043469213, 13507447059222749214u,
|
||||
"pm\t\t\t\t\t\t\t\t\t\t\t\005o\t\t\t\t\t\trr\375\375\375r\251r", 32766,
|
||||
16);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace upb
|
||||
1178
upb/message/convert_test.cc
Normal file
1178
upb/message/convert_test.cc
Normal file
File diff suppressed because it is too large
Load diff
148
upb/message/convert_test.proto
Normal file
148
upb/message/convert_test.proto
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
syntax = "proto2";
|
||||
|
||||
package upb.test.convert;
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_outer_classname = "ConvertTest";
|
||||
|
||||
message MessageWithExtension {
|
||||
extensions 1000 to max;
|
||||
}
|
||||
|
||||
extend MessageWithExtension {
|
||||
optional int32 ext_field_int32 = 1000;
|
||||
repeated MessageWithInt32 ext_field_repeated_msg = 1001;
|
||||
}
|
||||
|
||||
message MessageWithMapMessage {
|
||||
map<int32, MessageWithInt32> map_msg = 1;
|
||||
}
|
||||
|
||||
message MessageWithMapMessageClone {
|
||||
map<int32, MessageWithInt32Clone> map_msg = 1;
|
||||
}
|
||||
|
||||
message MessageWithKnown {
|
||||
optional int32 known_field_int32 = 1000;
|
||||
}
|
||||
|
||||
message MessageWithKnownInt64 {
|
||||
optional int64 known_field_int64 = 1000;
|
||||
}
|
||||
|
||||
message MessageWithKnownRepeatedMsg {
|
||||
repeated MessageWithInt32 known_repeated_msg = 1001;
|
||||
}
|
||||
|
||||
message MessageWithInt32 {
|
||||
optional int32 f1 = 1;
|
||||
}
|
||||
|
||||
message MessageWithInt32Clone {
|
||||
optional int32 f1 = 1;
|
||||
}
|
||||
|
||||
extend MessageWithExtension {
|
||||
optional MessageWithInt32 ext_field_msg = 1002;
|
||||
}
|
||||
|
||||
message MessageWithKnownMsg {
|
||||
optional MessageWithInt32 known_msg = 1002;
|
||||
}
|
||||
|
||||
message MessageWithKnownMsgClone {
|
||||
optional MessageWithInt32Clone known_msg = 1002;
|
||||
}
|
||||
|
||||
message MessageWithRepeatedMsg {
|
||||
repeated MessageWithInt32 msgs = 1;
|
||||
}
|
||||
|
||||
message MessageWithRepeatedMsgClone {
|
||||
repeated MessageWithInt32Clone msgs = 1;
|
||||
}
|
||||
|
||||
message MessageWithMsg {
|
||||
optional MessageWithInt32 msg = 1;
|
||||
}
|
||||
|
||||
message MessageWithMsgClone {
|
||||
optional MessageWithInt32Clone msg = 1;
|
||||
}
|
||||
|
||||
message MessageWithKnownRepeatedMsgClone {
|
||||
repeated MessageWithInt32Clone known_repeated_msg = 1001;
|
||||
}
|
||||
|
||||
message MessageWithInt64 {
|
||||
optional int64 f1 = 1;
|
||||
}
|
||||
|
||||
message MessageWithString {
|
||||
optional string f1 = 1;
|
||||
}
|
||||
|
||||
message MessageWithRepeatedInt32 {
|
||||
repeated int32 r = 1;
|
||||
}
|
||||
|
||||
message MessageWithRepeatedInt64 {
|
||||
repeated int64 r = 1;
|
||||
}
|
||||
|
||||
message MessageWithRepeatedString {
|
||||
repeated string r = 1;
|
||||
}
|
||||
|
||||
message MessageWithMapInt32Int32 {
|
||||
map<int32, int32> m = 1;
|
||||
}
|
||||
|
||||
message MessageWithMapInt32Int32Clone {
|
||||
map<int32, int32> m = 1;
|
||||
}
|
||||
|
||||
message MessageWithMapInt32Int64 {
|
||||
map<int32, int64> m = 1;
|
||||
}
|
||||
|
||||
message AnotherMessageWithExtension {
|
||||
extensions 1000 to max;
|
||||
}
|
||||
|
||||
extend AnotherMessageWithExtension {
|
||||
optional int32 another_ext_field_int32 = 1000;
|
||||
}
|
||||
|
||||
message SrcWithOneof {
|
||||
oneof my_oneof {
|
||||
int32 oneof_int32 = 1;
|
||||
string oneof_string = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message Proto2EnumMessage {
|
||||
enum NestedEnum {
|
||||
FOO = 0;
|
||||
BAR = 1;
|
||||
BAZ = 2;
|
||||
NEG = -1;
|
||||
}
|
||||
optional NestedEnum optional_nested_enum = 21;
|
||||
repeated NestedEnum repeated_nested_enum = 51;
|
||||
map<string, NestedEnum> map_string_nested_enum = 73;
|
||||
}
|
||||
|
||||
extend MessageWithExtension {
|
||||
optional Proto2EnumMessage.NestedEnum ext_enum = 2000;
|
||||
repeated Proto2EnumMessage.NestedEnum ext_repeated_enum = 2001;
|
||||
}
|
||||
|
||||
message MessageWithKnownEnum {
|
||||
optional Proto2EnumMessage.NestedEnum ext_enum = 2000;
|
||||
repeated Proto2EnumMessage.NestedEnum ext_repeated_enum = 2001;
|
||||
}
|
||||
|
||||
message MessageWithMapAt1000 {
|
||||
map<int32, int32> m = 1000;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue