Skip to content

Commit 3286f94

Browse files
Implements upb_Message_DeepClone.
PiperOrigin-RevId: 514723111
1 parent a6ce733 commit 3286f94

File tree

5 files changed

+647
-0
lines changed

5 files changed

+647
-0
lines changed

BUILD

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,26 @@ cc_library(
282282
],
283283
)
284284

285+
cc_library(
286+
name = "message_copy",
287+
srcs = [
288+
"upb/message/copy.c",
289+
],
290+
hdrs = [
291+
"upb/message/copy.h",
292+
],
293+
copts = UPB_DEFAULT_COPTS,
294+
visibility = ["//visibility:public"],
295+
deps = [
296+
":collections_internal",
297+
":message_accessors",
298+
":message_internal",
299+
":mini_table_internal",
300+
":port",
301+
":upb",
302+
],
303+
)
304+
285305
cc_test(
286306
name = "mini_table_encode_test",
287307
srcs = [
@@ -317,6 +337,24 @@ cc_test(
317337
],
318338
)
319339

340+
cc_test(
341+
name = "message_copy_test",
342+
srcs = ["upb/message/copy_test.cc"],
343+
deps = [
344+
":collections",
345+
":message_accessors",
346+
":message_copy",
347+
":mini_table_internal",
348+
":upb",
349+
"//upb/test:test_messages_proto2_upb_proto",
350+
"//upb/test:test_messages_proto3_upb_proto",
351+
"//upb/test:test_upb_proto",
352+
"@com_google_absl//absl/container:flat_hash_set",
353+
"@com_google_googletest//:gtest_main",
354+
"@com_google_protobuf//:protobuf",
355+
],
356+
)
357+
320358
cc_library(
321359
name = "fastdecode",
322360
copts = UPB_DEFAULT_COPTS,

protos/protos.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,12 @@ typename T::Proxy CreateMessage(::protos::Arena& arena) {
116116
arena.ptr());
117117
}
118118

119+
template <typename T>
120+
typename T::Proxy CloneMessage(Ptr<T> message, upb::Arena& arena) {
121+
return typename T::Proxy(
122+
upb_Message_DeepClone(message, T::minitable(), arena.ptr()), arena.ptr());
123+
}
124+
119125
// begin:github_only
120126
// This type exists to work around an absl type that has not yet been
121127
// released.

upb/message/copy.c

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
/*
2+
* Copyright (c) 2009-2021, Google LLC
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
* * Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* * Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
* * Neither the name of Google LLC nor the
13+
* names of its contributors may be used to endorse or promote products
14+
* derived from this software without specific prior written permission.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
20+
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23+
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
*/
27+
28+
#include "upb/message/copy.h"
29+
30+
#include "upb/mem/arena.h"
31+
#include "upb/message/accessors.h"
32+
#include "upb/message/message.h"
33+
34+
// Must be last.
35+
#include "upb/mini_table/common.h"
36+
#include "upb/port/def.inc"
37+
38+
static bool upb_MessageField_IsMap(const upb_MiniTableField* field) {
39+
return upb_FieldMode_Get(field) == kUpb_FieldMode_Map;
40+
}
41+
42+
static upb_StringView upb_Clone_StringView(upb_StringView str,
43+
upb_Arena* arena) {
44+
if (str.size == 0) {
45+
return upb_StringView_FromDataAndSize(NULL, 0);
46+
}
47+
void* cloned_data = upb_Arena_Malloc(arena, str.size);
48+
upb_StringView cloned_str =
49+
upb_StringView_FromDataAndSize(cloned_data, str.size);
50+
memcpy(cloned_data, str.data, str.size);
51+
return cloned_str;
52+
}
53+
54+
static bool upb_Clone_MessageValue(void* value, upb_CType value_type,
55+
const upb_MiniTable* sub, upb_Arena* arena) {
56+
switch (value_type) {
57+
case kUpb_CType_Bool:
58+
case kUpb_CType_Float:
59+
case kUpb_CType_Int32:
60+
case kUpb_CType_UInt32:
61+
case kUpb_CType_Enum:
62+
case kUpb_CType_Double:
63+
case kUpb_CType_Int64:
64+
case kUpb_CType_UInt64:
65+
return true;
66+
case kUpb_CType_String:
67+
case kUpb_CType_Bytes: {
68+
upb_StringView source = *(upb_StringView*)value;
69+
int size = source.size;
70+
void* cloned_data = upb_Arena_Malloc(arena, size);
71+
if (cloned_data == NULL) {
72+
return false;
73+
}
74+
*(upb_StringView*)value =
75+
upb_StringView_FromDataAndSize(cloned_data, size);
76+
memcpy(cloned_data, source.data, size);
77+
return true;
78+
} break;
79+
case kUpb_CType_Message: {
80+
UPB_ASSERT(sub);
81+
const upb_Message* source = *(upb_Message**)value;
82+
UPB_ASSERT(source);
83+
upb_Message* clone = upb_Message_DeepClone(source, sub, arena);
84+
*(upb_Message**)value = clone;
85+
return clone != NULL;
86+
} break;
87+
}
88+
UPB_UNREACHABLE();
89+
}
90+
91+
upb_Map* upb_Map_DeepClone(const upb_Map* map, upb_CType key_type,
92+
upb_CType value_type,
93+
const upb_MiniTable* map_entry_table,
94+
upb_Arena* arena) {
95+
upb_Map* cloned_map = _upb_Map_New(arena, map->key_size, map->val_size);
96+
if (cloned_map == NULL) {
97+
return NULL;
98+
}
99+
upb_MessageValue key, val;
100+
size_t iter = kUpb_Map_Begin;
101+
while (upb_Map_Next(map, &key, &val, &iter)) {
102+
const upb_MiniTableField* value_field = &map_entry_table->fields[1];
103+
const upb_MiniTable* value_sub =
104+
(value_field->submsg_index != kUpb_NoSub)
105+
? upb_MiniTable_GetSubMessageTable(map_entry_table, value_field)
106+
: NULL;
107+
upb_CType value_field_type = upb_MiniTableField_CType(value_field);
108+
if (!upb_Clone_MessageValue(&val, value_field_type, value_sub, arena)) {
109+
return NULL;
110+
}
111+
if (upb_Map_Insert(cloned_map, key, val, arena) ==
112+
kUpb_MapInsertStatus_OutOfMemory) {
113+
return NULL;
114+
}
115+
}
116+
return cloned_map;
117+
}
118+
119+
static upb_Map* upb_Message_Map_DeepClone(const upb_Map* map,
120+
const upb_MiniTable* mini_table,
121+
const upb_MiniTableField* field,
122+
upb_Message* clone,
123+
upb_Arena* arena) {
124+
const upb_MiniTable* map_entry_table =
125+
mini_table->subs[field->submsg_index].submsg;
126+
UPB_ASSERT(map_entry_table);
127+
128+
const upb_MiniTableField* key_field = &map_entry_table->fields[0];
129+
const upb_MiniTableField* value_field = &map_entry_table->fields[1];
130+
131+
upb_Map* cloned_map = upb_Map_DeepClone(
132+
map, upb_MiniTableField_CType(key_field),
133+
upb_MiniTableField_CType(value_field), map_entry_table, arena);
134+
if (!cloned_map) {
135+
return NULL;
136+
}
137+
_upb_Message_SetNonExtensionField(clone, field, &cloned_map);
138+
return cloned_map;
139+
}
140+
141+
upb_Array* upb_Array_DeepClone(const upb_Array* array, upb_CType value_type,
142+
const upb_MiniTable* sub, upb_Arena* arena) {
143+
size_t size = array->size;
144+
upb_Array* cloned_array =
145+
_upb_Array_New(arena, size, _upb_Array_CTypeSizeLg2(value_type));
146+
if (!cloned_array) {
147+
return NULL;
148+
}
149+
for (int i = 0; i < size; ++i) {
150+
upb_MessageValue val = upb_Array_Get(array, i);
151+
if (!upb_Clone_MessageValue(&val, value_type, sub, arena)) {
152+
return false;
153+
}
154+
upb_Array_Set(cloned_array, i, val);
155+
}
156+
return cloned_array;
157+
}
158+
159+
static bool upb_Message_Array_DeepClone(const upb_Array* array,
160+
const upb_MiniTable* mini_table,
161+
const upb_MiniTableField* field,
162+
upb_Message* clone, upb_Arena* arena) {
163+
_upb_MiniTableField_CheckIsArray(field);
164+
upb_Array* cloned_array = upb_Array_DeepClone(
165+
array, upb_MiniTableField_CType(field),
166+
field->submsg_index != kUpb_NoSub
167+
? upb_MiniTable_GetSubMessageTable(mini_table, field)
168+
: NULL,
169+
arena);
170+
171+
// Clear out upb_Array* due to parent memcpy.
172+
_upb_Message_SetNonExtensionField(clone, field, &cloned_array);
173+
return true;
174+
}
175+
176+
static bool upb_Clone_ExtensionValue(
177+
const upb_MiniTableExtension* mini_table_ext,
178+
const upb_Message_Extension* source, upb_Message_Extension* dest,
179+
upb_Arena* arena) {
180+
dest->data = source->data;
181+
return upb_Clone_MessageValue(
182+
&dest->data, upb_MiniTableField_CType(&mini_table_ext->field),
183+
mini_table_ext->sub.submsg, arena);
184+
}
185+
186+
// Deep clones a message using the provided target arena.
187+
//
188+
// Returns NULL on failure.
189+
upb_Message* upb_Message_DeepClone(const upb_Message* message,
190+
const upb_MiniTable* mini_table,
191+
upb_Arena* arena) {
192+
upb_Message* clone = upb_Message_New(mini_table, arena);
193+
upb_StringView empty_string = upb_StringView_FromDataAndSize(NULL, 0);
194+
// Only copy message area skipping upb_Message_Internal.
195+
memcpy(clone, message, mini_table->size);
196+
for (size_t i = 0; i < mini_table->field_count; ++i) {
197+
const upb_MiniTableField* field = &mini_table->fields[i];
198+
if (!upb_IsRepeatedOrMap(field)) {
199+
switch (field->descriptortype) {
200+
case kUpb_FieldType_Group:
201+
case kUpb_FieldType_Message: {
202+
const upb_Message* sub_message =
203+
upb_Message_GetMessage(message, field, NULL);
204+
if (sub_message != NULL) {
205+
const upb_MiniTable* sub_message_table =
206+
upb_MiniTable_GetSubMessageTable(mini_table, field);
207+
upb_Message* cloned_sub_message =
208+
upb_Message_DeepClone(sub_message, sub_message_table, arena);
209+
if (cloned_sub_message == NULL) {
210+
return NULL;
211+
}
212+
upb_Message_SetMessage(clone, mini_table, field,
213+
cloned_sub_message);
214+
}
215+
} break;
216+
case kUpb_FieldType_String:
217+
case kUpb_FieldType_Bytes: {
218+
upb_StringView str =
219+
upb_Message_GetString(message, field, empty_string);
220+
if (str.size != 0) {
221+
if (!upb_Message_SetString(
222+
clone, field, upb_Clone_StringView(str, arena), arena)) {
223+
return NULL;
224+
}
225+
}
226+
} break;
227+
default:
228+
// Scalar, already copied.
229+
break;
230+
}
231+
} else {
232+
if (upb_MessageField_IsMap(field)) {
233+
const upb_Map* map = upb_Message_GetMap(message, field);
234+
if (map != NULL) {
235+
if (!upb_Message_Map_DeepClone(map, mini_table, field, clone,
236+
arena)) {
237+
return NULL;
238+
}
239+
}
240+
} else {
241+
const upb_Array* array = upb_Message_GetArray(message, field);
242+
if (array != NULL) {
243+
if (!upb_Message_Array_DeepClone(array, mini_table, field, clone,
244+
arena)) {
245+
return NULL;
246+
}
247+
}
248+
}
249+
}
250+
}
251+
// Clone extensions.
252+
size_t ext_count;
253+
const upb_Message_Extension* ext = _upb_Message_Getexts(message, &ext_count);
254+
for (size_t i = 0; i < ext_count; ++i) {
255+
const upb_Message_Extension* msg_ext = &ext[i];
256+
upb_Message_Extension* cloned_ext =
257+
_upb_Message_GetOrCreateExtension(clone, msg_ext->ext, arena);
258+
if (!cloned_ext) {
259+
return NULL;
260+
}
261+
if (!upb_Clone_ExtensionValue(msg_ext->ext, msg_ext, cloned_ext, arena)) {
262+
return NULL;
263+
}
264+
}
265+
266+
// Clone unknowns.
267+
size_t unknown_size = 0;
268+
const char* ptr = upb_Message_GetUnknown(message, &unknown_size);
269+
if (unknown_size != 0) {
270+
UPB_ASSERT(ptr);
271+
// Make a copy into destination arena.
272+
void* cloned_unknowns = upb_Arena_Malloc(arena, unknown_size);
273+
if (cloned_unknowns == NULL) {
274+
return NULL;
275+
}
276+
memcpy(cloned_unknowns, ptr, unknown_size);
277+
if (!_upb_Message_AddUnknown(clone, cloned_unknowns, unknown_size, arena)) {
278+
return NULL;
279+
}
280+
}
281+
return clone;
282+
}

0 commit comments

Comments
 (0)