blob: 12ee37f6153f92b8970c1b1b78b7f5845a604b39 [file]
// 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 {
// Since this extension is not known in the destination schema, encode it
// as an unknown field.
// TODO - b/510055656: to handle this as a non-canonical extension
upb_Message_EncodeExtensionAsUnknown(&c->encoder, dst, dst_mt, ext, val,
depth, &c->err);
}
}
}
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) {
upb_ErrorHandler_ThrowError(&c->err, kUpb_ErrorCode_MaxDepthExceeded);
}
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);
}
if (upb_MiniTableField_IsInOneof(dst_next) &&
UPB_PRIVATE(_upb_Message_GetOneofCase)(dst, dst_next) != 0) {
// Since fields are processed in descending order, the first encountered
// oneof field is the one that wins. We ignore subsequent ones to match
// the encoding-then-decoding behavior.
} else {
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;
size_t wire_size;
upb_Arena* tmp_arena = upb_Arena_New();
// Compare the encoded/decoded round-trip of the original message to the
// converted message.
// 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);
// Compare the decoded message to the converted message.
UPB_ASSERT(upb_Message_IsEqual(decoded_msg, dst, dst_mt, 0));
upb_Arena_Free(tmp_arena);
}
#endif
upb_Decoder_Destroy(&c.decoder, arena);
UPB_PRIVATE(_upb_encstate_destroy)(&c.encoder);
return dst;
}