aboutsummaryrefslogtreecommitdiffhomepage
path: root/php/ext
diff options
context:
space:
mode:
authorGravatar Paul Yang <TeBoring@users.noreply.github.com>2017-03-01 10:39:48 -0800
committerGravatar GitHub <noreply@github.com>2017-03-01 10:39:48 -0800
commitbcbaabe53a8d661f5a473d2a157a4278ad8bf579 (patch)
tree173e7e07319f10451e2ec290c00cba89a34b647e /php/ext
parent7339fc04c49a055ec0688cd0cb24cf7ea64f7783 (diff)
Add mergeFrom method on Message (#2766)
This method merges the contents of the specified message into the current message. Singular fields that are set in the specified message overwrite the corresponding fields in the current message. Repeated fields are appended. Map fields key-value pairs are overritten. Singular/Oneof sub-messages are recursively merged. All overritten sub-messages are deep-copied.
Diffstat (limited to 'php/ext')
-rw-r--r--php/ext/google/protobuf/array.c1
-rw-r--r--php/ext/google/protobuf/encode_decode.c2
-rw-r--r--php/ext/google/protobuf/map.c4
-rw-r--r--php/ext/google/protobuf/message.c28
-rw-r--r--php/ext/google/protobuf/protobuf.h4
-rw-r--r--php/ext/google/protobuf/storage.c216
6 files changed, 251 insertions, 4 deletions
diff --git a/php/ext/google/protobuf/array.c b/php/ext/google/protobuf/array.c
index e4a88c39..63bb6d0a 100644
--- a/php/ext/google/protobuf/array.c
+++ b/php/ext/google/protobuf/array.c
@@ -106,6 +106,7 @@ void repeated_field_init(TSRMLS_D) {
repeated_field_handlers = PEMALLOC(zend_object_handlers);
memcpy(repeated_field_handlers, zend_get_std_object_handlers(),
sizeof(zend_object_handlers));
+ repeated_field_handlers->write_dimension = repeated_field_write_dimension;
repeated_field_handlers->get_gc = repeated_field_get_gc;
}
diff --git a/php/ext/google/protobuf/encode_decode.c b/php/ext/google/protobuf/encode_decode.c
index 57fc81d3..78b12a06 100644
--- a/php/ext/google/protobuf/encode_decode.c
+++ b/php/ext/google/protobuf/encode_decode.c
@@ -434,7 +434,7 @@ static void map_slot_key(upb_fieldtype_t type, const void* from,
}
static void map_slot_value(upb_fieldtype_t type, const void* from,
- upb_value* v) {
+ upb_value* v) {
size_t len;
void* to = upb_value_memory(v);
#ifndef NDEBUG
diff --git a/php/ext/google/protobuf/map.c b/php/ext/google/protobuf/map.c
index 35747b05..ab98879d 100644
--- a/php/ext/google/protobuf/map.c
+++ b/php/ext/google/protobuf/map.c
@@ -310,8 +310,8 @@ static bool map_field_write_dimension(zval *object, zval *key,
mem = upb_value_memory(&v);
memset(mem, 0, native_slot_size(intern->value_type));
- if (!native_slot_set(intern->value_type, intern->msg_ce, mem, value
- TSRMLS_CC)) {
+ if (!native_slot_set(intern->value_type, intern->msg_ce, mem,
+ value TSRMLS_CC)) {
return false;
}
#ifndef NDEBUG
diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c
index 46da9024..b35df311 100644
--- a/php/ext/google/protobuf/message.c
+++ b/php/ext/google/protobuf/message.c
@@ -43,6 +43,7 @@ static zend_function_entry message_methods[] = {
PHP_ME(Message, decode, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Message, jsonEncode, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Message, jsonDecode, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(Message, mergeFrom, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Message, readOneof, NULL, ZEND_ACC_PROTECTED)
PHP_ME(Message, writeOneof, NULL, ZEND_ACC_PROTECTED)
PHP_ME(Message, whichOneof, NULL, ZEND_ACC_PROTECTED)
@@ -209,6 +210,13 @@ static zend_object_value message_create(zend_class_entry* ce TSRMLS_DC) {
return return_value;
}
+void message_create_with_type(zend_class_entry* ce, zval** message TSRMLS_DC) {
+ MAKE_STD_ZVAL(*message);
+ Z_TYPE_PP(message) = IS_OBJECT;
+ Z_OBJVAL_PP(message) = ce->create_object(ce TSRMLS_CC);
+ Z_DELREF_PP(message);
+}
+
void build_class_from_descriptor(zval* php_descriptor TSRMLS_DC) {
Descriptor* desc = UNBOX(Descriptor, php_descriptor);
@@ -260,6 +268,26 @@ PHP_METHOD(Message, clear) {
msg->std.properties_table TSRMLS_CC);
}
+PHP_METHOD(Message, mergeFrom) {
+ zval* value;
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &value,
+ message_type) == FAILURE) {
+ return;
+ }
+
+ MessageHeader* from =
+ (MessageHeader*)zend_object_store_get_object(value TSRMLS_CC);
+ MessageHeader* to =
+ (MessageHeader*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ if(from->descriptor != to->descriptor) {
+ zend_error(E_USER_ERROR, "Cannot merge messages with different class.");
+ return;
+ }
+
+ layout_merge(from->descriptor->layout, from, to TSRMLS_CC);
+}
+
PHP_METHOD(Message, readOneof) {
long index;
diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h
index d4737fb9..1562bbaf 100644
--- a/php/ext/google/protobuf/protobuf.h
+++ b/php/ext/google/protobuf/protobuf.h
@@ -151,6 +151,7 @@ extern zend_class_entry* enum_descriptor_type;
// -----------------------------------------------------------------------------
void* message_data(void* msg);
+void message_create_with_type(zend_class_entry* ce, zval** message TSRMLS_DC);
// Build PHP class for given descriptor. Instead of building from scratch, this
// function modifies existing class which has been partially defined in PHP
@@ -240,11 +241,14 @@ zval* layout_get(MessageLayout* layout, const void* storage,
const upb_fielddef* field, zval** cache TSRMLS_DC);
void layout_set(MessageLayout* layout, MessageHeader* header,
const upb_fielddef* field, zval* val TSRMLS_DC);
+void layout_merge(MessageLayout* layout, MessageHeader* from,
+ MessageHeader* to TSRMLS_DC);
const char* layout_get_oneof_case(MessageLayout* layout, const void* storage,
const upb_oneofdef* oneof TSRMLS_DC);
void free_layout(MessageLayout* layout);
PHP_METHOD(Message, clear);
+PHP_METHOD(Message, mergeFrom);
PHP_METHOD(Message, readOneof);
PHP_METHOD(Message, writeOneof);
PHP_METHOD(Message, whichOneof);
diff --git a/php/ext/google/protobuf/storage.c b/php/ext/google/protobuf/storage.c
index 5e05b935..1b239ee3 100644
--- a/php/ext/google/protobuf/storage.c
+++ b/php/ext/google/protobuf/storage.c
@@ -57,6 +57,31 @@ size_t native_slot_size(upb_fieldtype_t type) {
}
}
+static bool native_slot_is_default(upb_fieldtype_t type, void* memory) {
+ switch (type) {
+#define CASE_TYPE(upb_type, c_type) \
+ case UPB_TYPE_##upb_type: { \
+ return DEREF(memory, c_type) == 0; \
+ }
+ CASE_TYPE(INT32, int32_t )
+ CASE_TYPE(UINT32, uint32_t)
+ CASE_TYPE(ENUM, int32_t )
+ CASE_TYPE(INT64, int64_t )
+ CASE_TYPE(UINT64, uint64_t)
+ CASE_TYPE(FLOAT, float )
+ CASE_TYPE(DOUBLE, double )
+ CASE_TYPE(BOOL, int8_t )
+
+#undef CASE_TYPE
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES:
+ return Z_STRLEN_PP(DEREF(memory, zval**)) == 0;
+ case UPB_TYPE_MESSAGE:
+ return Z_TYPE_PP(DEREF(memory, zval**)) == IS_NULL;
+ default: return false;
+ }
+}
+
bool native_slot_set(upb_fieldtype_t type, const zend_class_entry* klass,
void* memory, zval* value TSRMLS_DC) {
switch (type) {
@@ -499,7 +524,6 @@ void layout_init(MessageLayout* layout, void* storage,
repeated_field_create_with_type(repeated_field_type, field,
property_ptr TSRMLS_CC);
DEREF(memory, zval**) = property_ptr;
- property_ptr = NULL;
} else {
native_slot_init(upb_fielddef_type(field), memory, property_ptr);
}
@@ -601,6 +625,196 @@ void layout_set(MessageLayout* layout, MessageHeader* header,
}
}
+void layout_merge(MessageLayout* layout, MessageHeader* from,
+ MessageHeader* to TSRMLS_DC) {
+ int i, j;
+ upb_msg_field_iter it;
+
+ for (upb_msg_field_begin(&it, layout->msgdef), i = 0; !upb_msg_field_done(&it);
+ upb_msg_field_next(&it), i++) {
+ const upb_fielddef* field = upb_msg_iter_field(&it);
+
+ void* to_memory = slot_memory(layout, message_data(to), field);
+ void* from_memory = slot_memory(layout, message_data(from), field);
+
+ if (upb_fielddef_containingoneof(field)) {
+ uint32_t oneof_case_offset =
+ layout->fields[upb_fielddef_index(field)].case_offset +
+ sizeof(MessageHeader);
+ // For a oneof, check that this field is actually present -- skip all the
+ // below if not.
+ if (DEREF(((uint8_t*)from + oneof_case_offset), uint32_t) !=
+ upb_fielddef_number(field)) {
+ continue;
+ }
+ uint32_t* from_oneof_case = slot_oneof_case(layout, message_data(from), field);
+ uint32_t* to_oneof_case = slot_oneof_case(layout, message_data(to), field);
+
+ // For non-singular fields, the related memory needs to point to the
+ // actual zval in properties table first.
+ switch (upb_fielddef_type(field)) {
+ case UPB_TYPE_MESSAGE:
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES: {
+ int property_cache_index =
+ layout->fields[upb_fielddef_index(field)].cache_index;
+ DEREF(to_memory, zval**) =
+ &(to->std.properties_table)[property_cache_index];
+ break;
+ }
+ default:
+ break;
+ }
+
+ *to_oneof_case = *from_oneof_case;
+
+ // Otherwise, fall through to the appropriate singular-field handler
+ // below.
+ }
+
+ if (is_map_field(field)) {
+ int size, key_length, value_length;
+ MapIter map_it;
+
+ zval* to_map_php = *DEREF(to_memory, zval**);
+ zval* from_map_php = *DEREF(from_memory, zval**);
+ Map* to_map = zend_object_store_get_object(to_map_php TSRMLS_CC);
+ Map* from_map = zend_object_store_get_object(from_map_php TSRMLS_CC);
+
+ size = upb_strtable_count(&from_map->table);
+ if (size == 0) continue;
+
+ for (map_begin(from_map_php, &map_it TSRMLS_CC); !map_done(&map_it);
+ map_next(&map_it)) {
+ const char* key = map_iter_key(&map_it, &key_length);
+ upb_value value = map_iter_value(&map_it, &value_length);
+ void* mem = upb_value_memory(&value);
+ switch (to_map->value_type) {
+ case UPB_TYPE_MESSAGE: {
+ zval* new_message;
+ message_create_with_type(to_map->msg_ce, &new_message TSRMLS_CC);
+ Z_ADDREF_P(new_message);
+
+ zval* subdesc_php = get_ce_obj(to_map->msg_ce);
+ Descriptor* subdesc =
+ zend_object_store_get_object(subdesc_php TSRMLS_CC);
+ MessageHeader* sub_from =
+ (MessageHeader*)zend_object_store_get_object(DEREF(mem, zval*)
+ TSRMLS_CC);
+ MessageHeader* sub_to =
+ (MessageHeader*)zend_object_store_get_object(
+ new_message TSRMLS_CC);
+ layout_merge(subdesc->layout, sub_from, sub_to TSRMLS_CC);
+ DEREF(mem, zval*) = new_message;
+ break;
+ }
+ case UPB_TYPE_BYTES:
+ case UPB_TYPE_STRING:
+ Z_ADDREF_PP((zval**)mem);
+ break;
+ default:
+ break;
+ }
+ map_index_set(to_map, key, key_length, value);
+ }
+
+ } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
+ zval* to_array_php = *DEREF(to_memory, zval**);
+ zval* from_array_php = *DEREF(from_memory, zval**);
+ RepeatedField* to_array =
+ zend_object_store_get_object(to_array_php TSRMLS_CC);
+ RepeatedField* from_array =
+ zend_object_store_get_object(from_array_php TSRMLS_CC);
+
+ int size = zend_hash_num_elements(HASH_OF(from_array->array));
+ if (size > 0) {
+ for (j = 0; j < size; j++) {
+ void* memory = NULL;
+ zend_hash_index_find(HASH_OF(from_array->array), j, (void**)&memory);
+ switch (to_array->type) {
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES: {
+ zval* str;
+ MAKE_STD_ZVAL(str);
+ ZVAL_STRINGL(str, Z_STRVAL_PP((zval**)memory),
+ Z_STRLEN_PP((zval**)memory), 1);
+ memory = &str;
+ break;
+ }
+ case UPB_TYPE_MESSAGE: {
+ zval* new_message;
+ message_create_with_type(from_array->msg_ce, &new_message TSRMLS_CC);
+ Z_ADDREF_P(new_message);
+
+ zval* subdesc_php = get_ce_obj(from_array->msg_ce);
+ Descriptor* subdesc =
+ zend_object_store_get_object(subdesc_php TSRMLS_CC);
+ MessageHeader* sub_from =
+ (MessageHeader*)zend_object_store_get_object(
+ DEREF(memory, zval*) TSRMLS_CC);
+ MessageHeader* sub_to =
+ (MessageHeader*)zend_object_store_get_object(
+ new_message TSRMLS_CC);
+ layout_merge(subdesc->layout, sub_from, sub_to TSRMLS_CC);
+
+ memory = &new_message;
+ }
+ default:
+ break;
+ }
+ repeated_field_push_native(to_array, memory TSRMLS_CC);
+ }
+ }
+ } else {
+ upb_fieldtype_t type = upb_fielddef_type(field);
+ zend_class_entry *ce = NULL;
+ if (!native_slot_is_default(type, from_memory)) {
+ switch (type) {
+#define CASE_TYPE(upb_type, c_type) \
+ case UPB_TYPE_##upb_type: { \
+ DEREF(to_memory, c_type) = DEREF(from_memory, c_type); \
+ break; \
+ }
+ CASE_TYPE(INT32, int32_t)
+ CASE_TYPE(UINT32, uint32_t)
+ CASE_TYPE(ENUM, int32_t)
+ CASE_TYPE(INT64, int64_t)
+ CASE_TYPE(UINT64, uint64_t)
+ CASE_TYPE(FLOAT, float)
+ CASE_TYPE(DOUBLE, double)
+ CASE_TYPE(BOOL, int8_t)
+
+#undef CASE_TYPE
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES:
+ native_slot_set(type, NULL, value_memory(field, to_memory),
+ *DEREF(from_memory, zval**) TSRMLS_CC);
+ break;
+ case UPB_TYPE_MESSAGE: {
+ const upb_msgdef* msg = upb_fielddef_msgsubdef(field);
+ zval* desc_php = get_def_obj(msg);
+ Descriptor* desc = zend_object_store_get_object(desc_php TSRMLS_CC);
+ ce = desc->klass;
+ if (native_slot_is_default(type, to_memory)) {
+ zval* new_message = NULL;
+ message_create_with_type(ce, &new_message TSRMLS_CC);
+ native_slot_set(type, ce, value_memory(field, to_memory),
+ new_message TSRMLS_CC);
+ }
+ MessageHeader* sub_from =
+ (MessageHeader*)zend_object_store_get_object(
+ *DEREF(from_memory, zval**) TSRMLS_CC);
+ MessageHeader* sub_to =
+ (MessageHeader*)zend_object_store_get_object(
+ *DEREF(to_memory, zval**) TSRMLS_CC);
+ layout_merge(desc->layout, sub_from, sub_to TSRMLS_CC);
+ }
+ }
+ }
+ }
+ }
+}
+
const char* layout_get_oneof_case(MessageLayout* layout, const void* storage,
const upb_oneofdef* oneof TSRMLS_DC) {
upb_oneof_iter i;