Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazily Create Singular Wrapper Message #6833

Merged
merged 21 commits into from Nov 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
393 changes: 381 additions & 12 deletions php/ext/google/protobuf/encode_decode.c

Large diffs are not rendered by default.

135 changes: 129 additions & 6 deletions php/ext/google/protobuf/message.c
Expand Up @@ -55,6 +55,8 @@ static zend_function_entry message_methods[] = {
PHP_ME(Message, serializeToJsonString, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Message, mergeFromJsonString, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Message, mergeFrom, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Message, readWrapperValue, NULL, ZEND_ACC_PROTECTED)
PHP_ME(Message, writeWrapperValue, NULL, ZEND_ACC_PROTECTED)
PHP_ME(Message, readOneof, NULL, ZEND_ACC_PROTECTED)
PHP_ME(Message, writeOneof, NULL, ZEND_ACC_PROTECTED)
PHP_ME(Message, whichOneof, NULL, ZEND_ACC_PROTECTED)
Expand Down Expand Up @@ -295,12 +297,6 @@ void build_class_from_descriptor(
// PHP Methods
// -----------------------------------------------------------------------------

static bool is_wrapper_msg(const upb_msgdef* m) {
upb_wellknowntype_t type = upb_msgdef_wellknowntype(m);
return type >= UPB_WELLKNOWN_DOUBLEVALUE &&
type <= UPB_WELLKNOWN_BOOLVALUE;
}

static void append_wrapper_message(
zend_class_entry* subklass, RepeatedField* intern, zval* value TSRMLS_DC) {
MessageHeader* submsg;
Expand Down Expand Up @@ -561,6 +557,133 @@ PHP_METHOD(Message, mergeFrom) {
layout_merge(from->descriptor->layout, from, to TSRMLS_CC);
}

PHP_METHOD(Message, readWrapperValue) {
char* member;
PHP_PROTO_SIZE length;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &member,
&length) == FAILURE) {
return;
}

MessageHeader* msg = UNBOX(MessageHeader, getThis());
const upb_fielddef* field =
upb_msgdef_ntofz(msg->descriptor->msgdef, member);

if (upb_fielddef_containingoneof(field)) {
uint32_t* oneof_case =
slot_oneof_case(msg->descriptor->layout, message_data(msg), field);
if (*oneof_case != upb_fielddef_number(field)) {
RETURN_NULL();
}
}

zval* cached_zval =
CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, field));

if (Z_TYPE_P(cached_zval) == IS_NULL) {
RETURN_NULL();
}

if (Z_TYPE_P(cached_zval) == IS_OBJECT) {
const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field);
const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1);
MessageHeader* submsg = UNBOX(MessageHeader, cached_zval);
CACHED_VALUE* cached_value = find_zval_property(submsg, value_field);
layout_get(submsg->descriptor->layout, submsg, value_field,
cached_value TSRMLS_CC);
RETURN_ZVAL(CACHED_PTR_TO_ZVAL_PTR(cached_value), 1, 0);
} else {
RETURN_ZVAL(cached_zval, 1, 0);
}
}

PHP_METHOD(Message, writeWrapperValue) {
char* member;
PHP_PROTO_SIZE length;
zval* value;
if (zend_parse_parameters(
ZEND_NUM_ARGS() TSRMLS_CC, "sz", &member, &length, &value) ==
FAILURE) {
return;
}

MessageHeader* msg = UNBOX(MessageHeader, getThis());
const upb_fielddef* field = upb_msgdef_ntofz(msg->descriptor->msgdef, member);

zval* cached_zval =
CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, field));

if (Z_TYPE_P(value) == IS_NULL) {
MessageHeader* msg = UNBOX(MessageHeader, getThis());
layout_set(msg->descriptor->layout, msg,
field, value TSRMLS_CC);
return;
}

{
// Type Checking
const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field);
const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1);
upb_fieldtype_t type = upb_fielddef_type(value_field);
switch(type) {
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
if (!protobuf_convert_to_string(value)) {
return;
}
if (type == UPB_TYPE_STRING &&
!is_structurally_valid_utf8(Z_STRVAL_P(value), Z_STRLEN_P(value))) {
zend_error(E_USER_ERROR, "Given string is not UTF8 encoded.");
return;
}
}
break;
#define CASE_TYPE(upb_type, type, c_type) \
case UPB_TYPE_##upb_type: { \
c_type type##_value; \
if (!protobuf_convert_to_##type(value, &type##_value)) { \
return; \
} \
break; \
}
CASE_TYPE(INT32, int32, int32_t)
CASE_TYPE(UINT32, uint32, uint32_t)
CASE_TYPE(ENUM, int32, int32_t)
CASE_TYPE(INT64, int64, int64_t)
CASE_TYPE(UINT64, uint64, uint64_t)
CASE_TYPE(FLOAT, float, float)
CASE_TYPE(DOUBLE, double, double)
CASE_TYPE(BOOL, bool, int8_t)

#undef CASE_TYPE
}
}

if (upb_fielddef_containingoneof(field)) {
uint32_t* oneof_case =
slot_oneof_case(msg->descriptor->layout, message_data(msg), field);
if (*oneof_case != upb_fielddef_number(field)) {
zval null_value;
ZVAL_NULL(&null_value);
layout_set(msg->descriptor->layout, msg, field, &null_value TSRMLS_CC);
cached_zval = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, field));
ZVAL_ZVAL(cached_zval, value, 1, 0);
return;
}
}

if (Z_TYPE_P(cached_zval) == IS_OBJECT) {
const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field);
const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1);
MessageHeader* submsg = UNBOX(MessageHeader, cached_zval);
CACHED_VALUE* cached_value = find_zval_property(submsg, value_field);
layout_set(submsg->descriptor->layout, submsg,
value_field, value TSRMLS_CC);
} else {
ZVAL_ZVAL(cached_zval, value, 1, 0);
}
}

PHP_METHOD(Message, readOneof) {
PHP_PROTO_LONG index;

Expand Down
5 changes: 5 additions & 0 deletions php/ext/google/protobuf/protobuf.h
Expand Up @@ -965,6 +965,8 @@ void* slot_memory(MessageLayout* layout, const void* storage,

PHP_METHOD(Message, clear);
PHP_METHOD(Message, mergeFrom);
PHP_METHOD(Message, readWrapperValue);
PHP_METHOD(Message, writeWrapperValue);
PHP_METHOD(Message, readOneof);
PHP_METHOD(Message, writeOneof);
PHP_METHOD(Message, whichOneof);
Expand Down Expand Up @@ -1525,4 +1527,7 @@ static inline zval* php_proto_message_read_property(
bool is_reserved_name(const char* name);
bool is_valid_constant_name(const char* name);

// For lazy wrapper
bool is_wrapper_msg(const upb_msgdef* m);

#endif // __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__
113 changes: 62 additions & 51 deletions php/ext/google/protobuf/storage.c
Expand Up @@ -116,17 +116,17 @@ bool native_slot_set(upb_fieldtype_t type, const zend_class_entry* klass,
return false;
}

#if PHP_MAJOR_VERSION < 7
REPLACE_ZVAL_VALUE((CACHED_VALUE*)memory, value, 1);
#else
zval* property_ptr = CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)memory);
if (EXPECTED(property_ptr != value)) {
php_proto_zval_ptr_dtor(property_ptr);
}

#if PHP_MAJOR_VERSION < 7
DEREF(memory, zval*) = value;
Z_ADDREF_P(value);
#else
ZVAL_ZVAL(property_ptr, value, 1, 0);
#endif

break;
}

Expand Down Expand Up @@ -797,25 +797,62 @@ zval* layout_get(MessageLayout* layout, MessageHeader* header,
if (upb_fielddef_containingoneof(field)) {
if (*oneof_case != upb_fielddef_number(field)) {
native_slot_get_default(upb_fielddef_type(field), cache TSRMLS_CC);
} else {
upb_fieldtype_t type = upb_fielddef_type(field);
CACHED_VALUE* stored_cache = find_zval_property(header, field);
native_slot_get(
type, value_memory(type, memory, stored_cache), cache TSRMLS_CC);
return CACHED_PTR_TO_ZVAL_PTR(cache);
}
return CACHED_PTR_TO_ZVAL_PTR(cache);
// Intentional fall through to be handled as a signuarl field.
} else if (is_map_field(field)) {
map_field_ensure_created(field, cache PHP_PROTO_TSRMLS_CC);
return CACHED_PTR_TO_ZVAL_PTR(cache);
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
repeated_field_ensure_created(field, cache PHP_PROTO_TSRMLS_CC);
return CACHED_PTR_TO_ZVAL_PTR(cache);
}

CACHED_VALUE* stored_cache = find_zval_property(header, field);

if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE &&
is_wrapper_msg(upb_fielddef_msgsubdef(field))) {
zval * cached_zval = CACHED_PTR_TO_ZVAL_PTR(stored_cache);
#if PHP_MAJOR_VERSION >= 7
zend_object* obj;
#endif
if (Z_TYPE_P(cached_zval) != IS_OBJECT &&
Z_TYPE_P(cached_zval) != IS_NULL) {
// Needs to expand value to wrapper.
const upb_msgdef* submsgdef = upb_fielddef_msgsubdef(field);
const upb_fielddef* value_field = upb_msgdef_itof(submsgdef, 1);
MessageHeader* submsg;
Descriptor* subdesc =
UNBOX_HASHTABLE_VALUE(
Descriptor, get_def_obj((void*)submsgdef));
zend_class_entry* subklass = subdesc->klass;
#if PHP_MAJOR_VERSION < 7
zval* val = NULL;
MAKE_STD_ZVAL(val);
ZVAL_OBJ(val, subklass->create_object(subklass TSRMLS_CC));
submsg = UNBOX(MessageHeader, val);
#else
obj = subklass->create_object(subklass TSRMLS_CC);
submsg = (MessageHeader*)((char*)obj - XtOffsetOf(MessageHeader, std));
#endif
custom_data_init(subklass, submsg PHP_PROTO_TSRMLS_CC);

layout_set(subdesc->layout, submsg, value_field, cached_zval TSRMLS_CC);
#if PHP_MAJOR_VERSION < 7
ZVAL_ZVAL(cached_zval, val, 1, 1);
#else
ZVAL_OBJ(cached_zval, obj);
#endif
}
if (stored_cache != cache) {
ZVAL_ZVAL(CACHED_PTR_TO_ZVAL_PTR(cache), cached_zval, 1, 0);
}
} else {
upb_fieldtype_t type = upb_fielddef_type(field);
native_slot_get(type, value_memory(type, memory, cache),
native_slot_get(type, value_memory(type, memory, stored_cache),
cache TSRMLS_CC);
return CACHED_PTR_TO_ZVAL_PTR(cache);
}
return CACHED_PTR_TO_ZVAL_PTR(cache);
}

void layout_set(MessageLayout* layout, MessageHeader* header,
Expand All @@ -825,33 +862,6 @@ void layout_set(MessageLayout* layout, MessageHeader* header,
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);

if (upb_fielddef_containingoneof(field)) {
upb_fieldtype_t type = upb_fielddef_type(field);
zend_class_entry *ce = NULL;

// For non-singular fields, the related memory needs to point to the actual
// zval in properties table first.
switch (type) {
case UPB_TYPE_MESSAGE: {
const upb_msgdef* msg = upb_fielddef_msgsubdef(field);
Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msg));
ce = desc->klass;
// Intentionally fall through.
}
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
int property_cache_index =
header->descriptor->layout->fields[upb_fielddef_index(field)]
.cache_index;
DEREF(memory, CACHED_VALUE*) =
OBJ_PROP(&header->std, property_cache_index);
memory = DEREF(memory, CACHED_VALUE*);
break;
}
default:
break;
}

native_slot_set(type, ce, memory, val TSRMLS_CC);
*oneof_case = upb_fielddef_number(field);
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
// Works for both repeated and map fields
Expand Down Expand Up @@ -895,19 +905,20 @@ void layout_set(MessageLayout* layout, MessageHeader* header,
#endif
zval_dtor(&converted_value);
}
} else {
upb_fieldtype_t type = upb_fielddef_type(field);
zend_class_entry *ce = NULL;
if (type == UPB_TYPE_MESSAGE) {
const upb_msgdef* msg = upb_fielddef_msgsubdef(field);
Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msg));
ce = desc->klass;
}
CACHED_VALUE* cache = find_zval_property(header, field);
native_slot_set(
type, ce, value_memory(upb_fielddef_type(field), memory, cache),
val TSRMLS_CC);
return;
}

upb_fieldtype_t type = upb_fielddef_type(field);
zend_class_entry *ce = NULL;
if (type == UPB_TYPE_MESSAGE) {
const upb_msgdef* msg = upb_fielddef_msgsubdef(field);
Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msg));
ce = desc->klass;
}
CACHED_VALUE* cache = find_zval_property(header, field);
native_slot_set(
type, ce, value_memory(upb_fielddef_type(field), memory, cache),
val TSRMLS_CC);
}

static void native_slot_merge(
Expand Down