blob: b4c09fae627bf68533c167e1d5fb0c58487b4f50 [file]
// 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
#include "upb/message/copy.h"
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "upb/base/descriptor_constants.h"
#include "upb/base/string_view.h"
#include "upb/hash/common.h"
#include "upb/hash/int_table.h"
#include "upb/hash/str_table.h"
#include "upb/mem/arena.h"
#include "upb/message/accessors.h"
#include "upb/message/array.h"
#include "upb/message/internal/accessors.h"
#include "upb/message/internal/array.h"
#include "upb/message/internal/extension.h"
#include "upb/message/internal/map.h"
#include "upb/message/internal/message.h"
#include "upb/message/internal/types.h"
#include "upb/message/map.h"
#include "upb/message/message.h"
#include "upb/mini_table/extension.h"
#include "upb/mini_table/field.h"
#include "upb/mini_table/internal/field.h"
#include "upb/mini_table/internal/size_log2.h"
#include "upb/mini_table/message.h"
// Must be last.
#include "upb/port/def.inc"
static upb_StringView upb_Clone_StringView(upb_StringView str,
upb_Arena* arena) {
if (str.size == 0) {
return upb_StringView_FromDataAndSize(NULL, 0);
}
void* cloned_data = upb_Arena_Malloc(arena, str.size);
if (cloned_data == NULL) {
return upb_StringView_FromDataAndSize(NULL, 0);
}
memcpy(cloned_data, str.data, str.size);
return upb_StringView_FromDataAndSize(cloned_data, str.size);
}
static bool upb_Clone_MessageValue(void* value, upb_CType value_type,
const upb_MiniTable* sub, upb_Arena* arena) {
switch (value_type) {
case kUpb_CType_Bool:
case kUpb_CType_Float:
case kUpb_CType_Int32:
case kUpb_CType_UInt32:
case kUpb_CType_Enum:
case kUpb_CType_Double:
case kUpb_CType_Int64:
case kUpb_CType_UInt64:
return true;
case kUpb_CType_String:
case kUpb_CType_Bytes: {
upb_StringView source = *(upb_StringView*)value;
size_t size = source.size;
void* cloned_data = upb_Arena_Malloc(arena, size);
if (cloned_data == NULL) {
return false;
}
*(upb_StringView*)value =
upb_StringView_FromDataAndSize(cloned_data, size);
memcpy(cloned_data, source.data, size);
return true;
} break;
case kUpb_CType_Message: {
UPB_ASSERT(sub);
const upb_Message* source = *(upb_Message**)value;
UPB_ASSERT(source);
upb_Message* clone = upb_Message_DeepClone(source, sub, arena);
*(upb_Message**)value = clone;
return clone != NULL;
} break;
}
UPB_UNREACHABLE();
}
static bool upb_Clone_MapValue(upb_value* tabval, size_t map_val_size,
upb_CType value_field_type,
const upb_MiniTable* value_sub, upb_Arena* arena,
upb_value* cloned_tabval) {
upb_MessageValue val;
_upb_map_fromvalue(*tabval, &val, map_val_size);
if (!upb_Clone_MessageValue(&val, value_field_type, value_sub, arena)) {
return false;
}
*cloned_tabval = (upb_value){0}; // Initialize
if (!_upb_map_tovalue(&val, map_val_size, cloned_tabval, arena)) {
return false;
}
return true;
}
upb_Map* upb_Map_DeepClone(const upb_Map* map, upb_CType key_type,
upb_CType value_type,
const upb_MiniTable* map_entry_table,
upb_Arena* arena) {
upb_Map* cloned_map = upb_Arena_Malloc(arena, sizeof(upb_Map));
if (cloned_map == NULL) {
return NULL;
}
cloned_map->key_size = map->key_size;
cloned_map->val_size = map->val_size;
cloned_map->UPB_PRIVATE(is_frozen) = false;
cloned_map->UPB_PRIVATE(is_strtable) = map->UPB_PRIVATE(is_strtable);
const upb_MiniTableField* value_field =
upb_MiniTable_MapValue(map_entry_table);
const upb_MiniTable* value_sub = upb_MiniTable_SubMessage(value_field);
upb_CType value_field_type = upb_MiniTableField_CType(value_field);
bool is_primitive = value_field_type != kUpb_CType_Message &&
value_field_type != kUpb_CType_String &&
value_field_type != kUpb_CType_Bytes;
if (map->UPB_PRIVATE(is_strtable)) {
if (!upb_strtable_copy(&cloned_map->t.strtable, &map->t.strtable, arena)) {
return NULL;
}
if (!is_primitive) {
intptr_t iter = UPB_STRTABLE_BEGIN;
upb_StringView key;
upb_value tabval;
while (
upb_strtable_next2(&cloned_map->t.strtable, &key, &tabval, &iter)) {
upb_value cloned_tabval;
if (!upb_Clone_MapValue(&tabval, map->val_size, value_field_type,
value_sub, arena, &cloned_tabval)) {
return NULL;
}
upb_strtable_setentryvalue(&cloned_map->t.strtable, iter,
cloned_tabval);
}
}
} else {
if (!upb_inttable_copy(&cloned_map->t.inttable, &map->t.inttable, arena)) {
return NULL;
}
if (!is_primitive) {
intptr_t iter = UPB_INTTABLE_BEGIN;
uintptr_t key;
upb_value tabval;
while (upb_inttable_next(&cloned_map->t.inttable, &key, &tabval, &iter)) {
upb_value cloned_tabval;
if (!upb_Clone_MapValue(&tabval, map->val_size, value_field_type,
value_sub, arena, &cloned_tabval)) {
return NULL;
}
upb_inttable_setentryvalue(&cloned_map->t.inttable, iter,
cloned_tabval);
}
}
}
return cloned_map;
}
static upb_Map* upb_Message_Map_DeepClone(const upb_Map* map,
const upb_MiniTable* mini_table,
const upb_MiniTableField* f,
upb_Message* clone,
upb_Arena* arena) {
UPB_ASSERT(!upb_Message_IsFrozen(clone));
const upb_MiniTable* map_entry_table = upb_MiniTable_MapEntrySubMessage(f);
UPB_ASSERT(map_entry_table);
const upb_MiniTableField* key_field = upb_MiniTable_MapKey(map_entry_table);
const upb_MiniTableField* value_field =
upb_MiniTable_MapValue(map_entry_table);
upb_Map* cloned_map = upb_Map_DeepClone(
map, upb_MiniTableField_CType(key_field),
upb_MiniTableField_CType(value_field), map_entry_table, arena);
if (!cloned_map) {
return NULL;
}
upb_Message_SetBaseField(clone, f, &cloned_map);
return cloned_map;
}
upb_Array* upb_Array_DeepClone(const upb_Array* array, upb_CType value_type,
const upb_MiniTable* sub, upb_Arena* arena) {
const size_t size = upb_Array_Size(array);
const int lg2 = UPB_PRIVATE(_upb_CType_SizeLg2)(value_type);
upb_Array* cloned_array = UPB_PRIVATE(_upb_Array_New)(arena, size, lg2);
if (!cloned_array) {
return NULL;
}
if (!UPB_PRIVATE(_upb_Array_ResizeUninitialized)(cloned_array, size, arena)) {
return NULL;
}
for (size_t i = 0; i < size; ++i) {
upb_MessageValue val = upb_Array_Get(array, i);
if (!upb_Clone_MessageValue(&val, value_type, sub, arena)) {
return NULL;
}
upb_Array_Set(cloned_array, i, val);
}
return cloned_array;
}
static bool upb_Message_Array_DeepClone(const upb_Array* array,
const upb_MiniTable* mini_table,
const upb_MiniTableField* field,
upb_Message* clone, upb_Arena* arena) {
UPB_ASSERT(!upb_Message_IsFrozen(clone));
UPB_PRIVATE(_upb_MiniTableField_CheckIsArray)(field);
upb_Array* cloned_array =
upb_Array_DeepClone(array, upb_MiniTableField_CType(field),
upb_MiniTableField_CType(field) == kUpb_CType_Message
? upb_MiniTable_GetSubMessageTable(field)
: NULL,
arena);
if (!cloned_array) return false;
// Clear out upb_Array* due to parent memcpy.
upb_Message_SetBaseField(clone, field, &cloned_array);
return true;
}
static bool upb_Clone_ExtensionValue(const upb_MiniTableField* field,
const upb_Extension* source,
upb_Extension* dest, upb_Arena* arena) {
upb_MessageValue val = upb_Extension_GetValue(source);
if (!upb_Clone_MessageValue(&val, upb_MiniTableField_CType(field),
upb_MiniTable_SubMessage(field), arena)) {
return false;
}
upb_Extension_SetValue(dest, val);
return true;
}
upb_Message* _upb_Message_Copy(upb_Message* dst, const upb_Message* src,
const upb_MiniTable* mini_table,
upb_Arena* arena) {
UPB_ASSERT(!upb_Message_IsFrozen(dst));
upb_StringView empty_string = upb_StringView_FromDataAndSize(NULL, 0);
// Only copy message area skipping upb_Message_Internal.
memcpy(dst + 1, src + 1, mini_table->UPB_PRIVATE(size) - sizeof(upb_Message));
for (int i = 0; i < upb_MiniTable_FieldCount(mini_table); ++i) {
const upb_MiniTableField* field =
upb_MiniTable_GetFieldByIndex(mini_table, i);
if (upb_MiniTableField_IsScalar(field)) {
switch (upb_MiniTableField_CType(field)) {
case kUpb_CType_Message: {
const upb_Message* sub_message = upb_Message_GetMessage(src, field);
if (sub_message != NULL) {
const upb_MiniTable* sub_message_table =
upb_MiniTable_GetSubMessageTable(field);
upb_Message* dst_sub_message =
upb_Message_DeepClone(sub_message, sub_message_table, arena);
if (dst_sub_message == NULL) goto err;
upb_Message_SetBaseFieldMessage(dst, field, dst_sub_message);
}
} break;
case kUpb_CType_String:
case kUpb_CType_Bytes: {
upb_StringView str = upb_Message_GetString(src, field, empty_string);
if (str.size != 0) {
upb_StringView cloned_str = upb_Clone_StringView(str, arena);
if (cloned_str.data == NULL) goto err;
if (!upb_Message_SetString(dst, field, cloned_str, arena)) goto err;
}
} break;
default:
// Scalar, already copied.
break;
}
} else {
if (upb_MiniTableField_IsMap(field)) {
const upb_Map* map = upb_Message_GetMap(src, field);
if (map != NULL) {
if (!upb_Message_Map_DeepClone(map, mini_table, field, dst, arena)) {
goto err;
}
}
} else {
const upb_Array* array = upb_Message_GetArray(src, field);
if (array != NULL) {
if (!upb_Message_Array_DeepClone(array, mini_table, field, dst,
arena)) {
goto err;
}
}
}
}
}
// Clone extensions.
upb_Message_Internal* in = UPB_PRIVATE(_upb_Message_GetInternal)(src);
if (!in) return dst;
for (size_t i = 0; i < in->size; i++) {
upb_TaggedAuxPtr tagged_ptr = in->aux_data[i];
if (upb_TaggedAuxPtr_IsExtension(tagged_ptr)) {
// Clone a canonical or non-canonical upb_Extension*.
const upb_Extension* msg_ext = upb_TaggedAuxPtr_Extension(tagged_ptr);
const upb_MiniTableField* field = upb_Extension_MiniTableField(msg_ext);
upb_Extension* dst_ext =
UPB_PRIVATE(_upb_Message_GetOrCreateExtensionWithTag)(
dst, upb_Extension_MiniTableExtension(msg_ext), arena,
upb_TaggedAuxPtr_Type(tagged_ptr));
if (!dst_ext) goto err;
if (upb_MiniTableField_IsScalar(field)) {
if (!upb_Clone_ExtensionValue(field, msg_ext, dst_ext, arena)) {
goto err;
}
} else {
// Extensions (canonical or non-canonical) cannot be maps.
UPB_ASSERT(upb_MiniTableField_IsArray(field));
const upb_Array* msg_array = upb_Extension_GetArray(msg_ext);
UPB_ASSERT(msg_array);
upb_Array* cloned_array =
upb_Array_DeepClone(msg_array, upb_MiniTableField_CType(field),
upb_MiniTable_SubMessage(field), arena);
if (!cloned_array) goto err;
upb_Extension_SetArray(dst_ext, cloned_array);
}
} else if (upb_TaggedAuxPtr_IsUnknownStringView(tagged_ptr)) {
// Clone an aliased or non-aliased unknown upb_StringView.
upb_StringView* unknown = upb_TaggedPtrAux_StringViewRepr(tagged_ptr);
// Make a copy into destination arena.
if (!UPB_PRIVATE(_upb_Message_AddUnknown)(
dst, unknown->data, unknown->size, arena, kUpb_AddUnknown_Copy)) {
goto err;
}
}
}
return dst;
err:
upb_Message_Clear(dst, mini_table);
return NULL;
}
bool upb_Message_DeepCopy(upb_Message* dst, const upb_Message* src,
const upb_MiniTable* mini_table, upb_Arena* arena) {
UPB_ASSERT(!upb_Message_IsFrozen(dst));
upb_Message_Clear(dst, mini_table);
return _upb_Message_Copy(dst, src, mini_table, arena) != NULL;
}
// Deep clones a message using the provided target arena.
//
// Returns NULL on failure.
upb_Message* upb_Message_DeepClone(const upb_Message* msg,
const upb_MiniTable* m, upb_Arena* arena) {
upb_Message* clone = upb_Message_New(m, arena);
if (!clone) return NULL;
return _upb_Message_Copy(clone, msg, m, arena);
}
// Performs a shallow copy.
bool upb_Message_ShallowCopy(upb_Message* dst, const upb_Message* src,
const upb_MiniTable* m, upb_Arena* arena) {
UPB_ASSERT(!upb_Message_IsFrozen(dst));
memcpy(dst, src, m->UPB_PRIVATE(size));
return UPB_PRIVATE(_upb_Message_CopyInternal)(dst, src, arena);
}
// Performs a shallow clone.
upb_Message* upb_Message_ShallowClone(const upb_Message* msg,
const upb_MiniTable* m,
upb_Arena* arena) {
upb_Message* clone = upb_Message_New(m, arena);
if (!clone) return NULL;
if (!upb_Message_ShallowCopy(clone, msg, m, arena)) return NULL;
return clone;
}