Avoid too much overhead in layout_init (#6716)

* Avoid initializing primitive fields in layout_init

* Avoid initializing string/bytes/message fields in layout_init

* Lazily create map when needed

* Lazily create repeated fields

* Change layout_init to only do memcpy

* Fix test for php-7.0

* Fix conformance test where default value of string/message map is not encoded

* Fix test for zts

* Clean up

* Fix comments
pull/6820/head
Paul Yang 2019-10-29 12:48:24 -07:00 committed by GitHub
parent 3fa17ca0bd
commit c53e5b8e11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 271 additions and 164 deletions

2
.gitignore vendored
View File

@ -139,6 +139,8 @@ composer.lock
php/tests/generated/
php/tests/old_protoc
php/tests/protobuf/
php/tests/core
php/tests/vgcore*
php/ext/google/protobuf/.libs/
php/ext/google/protobuf/Makefile.fragments
php/ext/google/protobuf/Makefile.global

View File

@ -259,6 +259,19 @@ void repeated_field_push_native(RepeatedField *intern, void *value) {
}
}
void repeated_field_ensure_created(
const upb_fielddef *field,
CACHED_VALUE *repeated_field PHP_PROTO_TSRMLS_DC) {
if (ZVAL_IS_NULL(CACHED_PTR_TO_ZVAL_PTR(repeated_field))) {
zval_ptr_dtor(repeated_field);
#if PHP_MAJOR_VERSION < 7
MAKE_STD_ZVAL(CACHED_PTR_TO_ZVAL_PTR(repeated_field));
#endif
repeated_field_create_with_field(repeated_field_type, field,
repeated_field PHP_PROTO_TSRMLS_CC);
}
}
void repeated_field_create_with_field(
zend_class_entry *ce, const upb_fielddef *field,
CACHED_VALUE *repeated_field PHP_PROTO_TSRMLS_DC) {

View File

@ -139,6 +139,15 @@ static const void* newhandlerdata(upb_handlers* h, uint32_t ofs) {
return hd_ofs;
}
static const void* newhandlerfielddata(
upb_handlers* h, const upb_fielddef* field) {
const void** hd_field = malloc(sizeof(void*));
PHP_PROTO_ASSERT(hd_field != NULL);
*hd_field = field;
upb_handlers_addcleanup(h, hd_field, free);
return hd_field;
}
typedef struct {
void* closure;
stringsink sink;
@ -163,16 +172,18 @@ static const void *newunknownfieldshandlerdata(upb_handlers* h) {
}
typedef struct {
const upb_fielddef *fd;
size_t ofs;
const upb_msgdef *md;
} submsg_handlerdata_t;
// Creates a handlerdata that contains offset and submessage type information.
// Creates a handlerdata that contains field and submessage type information.
static const void *newsubmsghandlerdata(upb_handlers* h, uint32_t ofs,
const upb_fielddef* f) {
submsg_handlerdata_t* hd =
(submsg_handlerdata_t*)malloc(sizeof(submsg_handlerdata_t));
PHP_PROTO_ASSERT(hd != NULL);
hd->fd = f;
hd->ofs = ofs;
hd->md = upb_fielddef_msgsubdef(f);
upb_handlers_addcleanup(h, hd, free);
@ -221,8 +232,11 @@ static const void *newoneofhandlerdata(upb_handlers *h,
// this field (such an instance always exists even in an empty message).
static void *startseq_handler(void* closure, const void* hd) {
MessageHeader* msg = closure;
const size_t *ofs = hd;
return CACHED_PTR_TO_ZVAL_PTR(DEREF(message_data(msg), *ofs, CACHED_VALUE*));
const upb_fielddef** field = (const upb_fielddef**) hd;
CACHED_VALUE* cache = find_zval_property(msg, *field);
TSRMLS_FETCH();
repeated_field_ensure_created(*field, cache PHP_PROTO_TSRMLS_CC);
return CACHED_PTR_TO_ZVAL_PTR(cache);
}
// Handlers that append primitive values to a repeated field.
@ -322,15 +336,6 @@ static void *empty_php_string(zval* value_ptr) {
}
#endif
#if PHP_MAJOR_VERSION < 7
static void *empty_php_string2(zval** value_ptr) {
SEPARATE_ZVAL_IF_NOT_REF(value_ptr);
if (Z_TYPE_PP(value_ptr) == IS_STRING &&
!IS_INTERNED(Z_STRVAL_PP(value_ptr))) {
FREE(Z_STRVAL_PP(value_ptr));
}
ZVAL_EMPTY_STRING(*value_ptr);
return (void*)(*value_ptr);
}
static void new_php_string(zval** value_ptr, const char* str, size_t len) {
SEPARATE_ZVAL_IF_NOT_REF(value_ptr);
if (Z_TYPE_PP(value_ptr) == IS_STRING &&
@ -340,13 +345,6 @@ static void new_php_string(zval** value_ptr, const char* str, size_t len) {
ZVAL_STRINGL(*value_ptr, str, len, 1);
}
#else
static void *empty_php_string2(zval* value_ptr) {
if (Z_TYPE_P(value_ptr) == IS_STRING) {
zend_string_release(Z_STR_P(value_ptr));
}
ZVAL_EMPTY_STRING(value_ptr);
return value_ptr;
}
static void new_php_string(zval* value_ptr, const char* str, size_t len) {
if (Z_TYPE_P(value_ptr) == IS_STRING) {
zend_string_release(Z_STR_P(value_ptr));
@ -371,6 +369,21 @@ static void* str_handler(void *closure,
}
static bool str_end_handler(void *closure, const void *hd) {
stringfields_parseframe_t* frame = closure;
const upb_fielddef **field = (const upb_fielddef **) hd;
MessageHeader* msg = (MessageHeader*)frame->closure;
CACHED_VALUE* cached = find_zval_property(msg, *field);
new_php_string(cached, frame->sink.ptr, frame->sink.len);
stringsink_uninit(&frame->sink);
free(frame);
return true;
}
static bool map_str_end_handler(void *closure, const void *hd) {
stringfields_parseframe_t* frame = closure;
const size_t *ofs = hd;
MessageHeader* msg = (MessageHeader*)frame->closure;
@ -430,26 +443,60 @@ static void *submsg_handler(void *closure, const void *hd) {
zval* submsg_php;
MessageHeader* submsg;
if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(DEREF(message_data(msg), submsgdata->ofs,
CACHED_VALUE*))) == IS_NULL) {
CACHED_VALUE* cached = find_zval_property(msg, submsgdata->fd);
if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(cached)) == IS_NULL) {
#if PHP_MAJOR_VERSION < 7
zval val;
ZVAL_OBJ(&val, subklass->create_object(subklass TSRMLS_CC));
MessageHeader* intern = UNBOX(MessageHeader, &val);
custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC);
REPLACE_ZVAL_VALUE(DEREF(message_data(msg), submsgdata->ofs, zval**),
&val, 1);
REPLACE_ZVAL_VALUE(cached, &val, 1);
zval_dtor(&val);
#else
zend_object* obj = subklass->create_object(subklass TSRMLS_CC);
ZVAL_OBJ(DEREF(message_data(msg), submsgdata->ofs, zval*), obj);
ZVAL_OBJ(cached, obj);
MessageHeader* intern = UNBOX_HASHTABLE_VALUE(MessageHeader, obj);
custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC);
#endif
}
submsg_php = CACHED_PTR_TO_ZVAL_PTR(
DEREF(message_data(msg), submsgdata->ofs, CACHED_VALUE*));
submsg_php = CACHED_PTR_TO_ZVAL_PTR(cached);
submsg = UNBOX(MessageHeader, submsg_php);
return submsg;
}
static void *map_submsg_handler(void *closure, const void *hd) {
MessageHeader* msg = closure;
const submsg_handlerdata_t* submsgdata = hd;
TSRMLS_FETCH();
Descriptor* subdesc =
UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj((void*)submsgdata->md));
zend_class_entry* subklass = subdesc->klass;
zval* submsg_php;
MessageHeader* submsg;
CACHED_VALUE* cached =
DEREF(message_data(msg), submsgdata->ofs, CACHED_VALUE*);
if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(cached)) == IS_NULL) {
#if PHP_MAJOR_VERSION < 7
zval val;
ZVAL_OBJ(&val, subklass->create_object(subklass TSRMLS_CC));
MessageHeader* intern = UNBOX(MessageHeader, &val);
custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC);
REPLACE_ZVAL_VALUE(cached, &val, 1);
zval_dtor(&val);
#else
zend_object* obj = subklass->create_object(subklass TSRMLS_CC);
ZVAL_OBJ(cached, obj);
MessageHeader* intern = UNBOX_HASHTABLE_VALUE(MessageHeader, obj);
custom_data_init(subklass, intern PHP_PROTO_TSRMLS_CC);
#endif
}
submsg_php = CACHED_PTR_TO_ZVAL_PTR(cached);
submsg = UNBOX(MessageHeader, submsg_php);
return submsg;
@ -457,7 +504,7 @@ static void *submsg_handler(void *closure, const void *hd) {
// Handler data for startmap/endmap handlers.
typedef struct {
size_t ofs;
const upb_fielddef* fd;
const upb_msgdef* value_md;
upb_fieldtype_t key_field_type;
upb_fieldtype_t value_field_type;
@ -612,9 +659,10 @@ static void map_slot_value(upb_fieldtype_t type, const void* from,
static void *startmapentry_handler(void *closure, const void *hd) {
MessageHeader* msg = closure;
const map_handlerdata_t* mapdata = hd;
CACHED_VALUE* cache = find_zval_property(msg, mapdata->fd);
TSRMLS_FETCH();
zval* map = CACHED_PTR_TO_ZVAL_PTR(
DEREF(message_data(msg), mapdata->ofs, CACHED_VALUE*));
map_field_ensure_created(mapdata->fd, cache PHP_PROTO_TSRMLS_CC);
zval* map = CACHED_PTR_TO_ZVAL_PTR(cache);
map_parse_frame_t* frame = ALLOC(map_parse_frame_t);
frame->data = ALLOC(map_parse_frame_data_t);
@ -662,7 +710,7 @@ static bool endmap_handler(void* closure, const void* hd, upb_status* s) {
// key/value and endmsg handlers. The reason is that there is no easy way to
// pass the handlerdata down to the sub-message handler setup.
static map_handlerdata_t* new_map_handlerdata(
size_t ofs,
const upb_fielddef* field,
const upb_msgdef* mapentry_def,
Descriptor* desc) {
const upb_fielddef* key_field;
@ -671,7 +719,7 @@ static map_handlerdata_t* new_map_handlerdata(
map_handlerdata_t* hd =
(map_handlerdata_t*)malloc(sizeof(map_handlerdata_t));
PHP_PROTO_ASSERT(hd != NULL);
hd->ofs = ofs;
hd->fd = field;
key_field = upb_msgdef_itof(mapentry_def, MAP_KEY_FIELD);
PHP_PROTO_ASSERT(key_field != NULL);
hd->key_field_type = upb_fielddef_type(key_field);
@ -844,7 +892,7 @@ static void add_handlers_for_repeated_field(upb_handlers *h,
const upb_fielddef *f,
size_t offset) {
upb_handlerattr attr = UPB_HANDLERATTR_INIT;
attr.handler_data = newhandlerdata(h, offset);
attr.handler_data = newhandlerfielddata(h, f);
upb_handlers_setstartseq(h, f, startseq_handler, &attr);
switch (upb_fielddef_type(f)) {
@ -884,7 +932,7 @@ static void add_handlers_for_repeated_field(upb_handlers *h,
// Set up handlers for a singular field.
static void add_handlers_for_singular_field(upb_handlers *h,
const upb_fielddef *f,
size_t offset) {
size_t offset, bool is_map) {
switch (upb_fielddef_type(f)) {
#define SET_HANDLER(utype, ltype) \
case utype: { \
@ -908,16 +956,29 @@ static void add_handlers_for_singular_field(upb_handlers *h,
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
upb_handlerattr attr = UPB_HANDLERATTR_INIT;
attr.handler_data = newhandlerdata(h, offset);
if (is_map) {
attr.handler_data = newhandlerdata(h, offset);
} else {
attr.handler_data = newhandlerfielddata(h, f);
}
upb_handlers_setstartstr(h, f, str_handler, &attr);
upb_handlers_setstring(h, f, stringdata_handler, &attr);
upb_handlers_setendstr(h, f, str_end_handler, &attr);
if (is_map) {
upb_handlers_setendstr(h, f, map_str_end_handler, &attr);
} else {
upb_handlers_setendstr(h, f, str_end_handler, &attr);
}
break;
}
case UPB_TYPE_MESSAGE: {
upb_handlerattr attr = UPB_HANDLERATTR_INIT;
attr.handler_data = newsubmsghandlerdata(h, offset, f);
upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr);
if (is_map) {
attr.handler_data = newsubmsghandlerdata(h, offset, f);
upb_handlers_setstartsubmsg(h, f, map_submsg_handler, &attr);
} else {
attr.handler_data = newsubmsghandlerdata(h, 0, f);
upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr);
}
break;
}
}
@ -929,7 +990,7 @@ static void add_handlers_for_mapfield(upb_handlers* h,
size_t offset,
Descriptor* desc) {
const upb_msgdef* map_msgdef = upb_fielddef_msgsubdef(fielddef);
map_handlerdata_t* hd = new_map_handlerdata(offset, map_msgdef, desc);
map_handlerdata_t* hd = new_map_handlerdata(fielddef, map_msgdef, desc);
upb_handlerattr attr = UPB_HANDLERATTR_INIT;
upb_handlers_addcleanup(h, hd, free);
@ -951,10 +1012,10 @@ static void add_handlers_for_mapentry(const upb_msgdef* msgdef, upb_handlers* h,
add_handlers_for_singular_field(h, key_field,
offsetof(map_parse_frame_data_t,
key_storage));
key_storage), true);
add_handlers_for_singular_field(h, value_field,
offsetof(map_parse_frame_data_t,
value_storage));
value_storage), true);
}
// Set up handlers for a oneof field.
@ -1063,7 +1124,7 @@ void add_handlers_for_message(const void* closure, upb_handlers* h) {
} else if (upb_fielddef_isseq(f)) {
add_handlers_for_repeated_field(h, f, offset);
} else {
add_handlers_for_singular_field(h, f, offset);
add_handlers_for_singular_field(h, f, offset, false);
}
}
}
@ -1259,16 +1320,13 @@ static void putjsonany(MessageHeader* msg, const Descriptor* desc,
const upb_fielddef* type_field = upb_msgdef_itof(desc->msgdef, UPB_ANY_TYPE);
const upb_fielddef* value_field = upb_msgdef_itof(desc->msgdef, UPB_ANY_VALUE);
uint32_t type_url_offset;
zval* type_url_php_str;
const upb_msgdef *payload_type = NULL;
upb_sink_startmsg(sink);
/* Handle type url */
type_url_offset = desc->layout->fields[upb_fielddef_index(type_field)].offset;
type_url_php_str = CACHED_PTR_TO_ZVAL_PTR(
DEREF(message_data(msg), type_url_offset, CACHED_VALUE*));
type_url_php_str = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, type_field));
if (Z_STRLEN_P(type_url_php_str) > 0) {
putstr(type_url_php_str, type_field, sink, false);
}
@ -1294,14 +1352,11 @@ static void putjsonany(MessageHeader* msg, const Descriptor* desc,
}
{
uint32_t value_offset;
zval* value_php_str;
const char* value_str;
size_t value_len;
value_offset = desc->layout->fields[upb_fielddef_index(value_field)].offset;
value_php_str = CACHED_PTR_TO_ZVAL_PTR(
DEREF(message_data(msg), value_offset, CACHED_VALUE*));
value_php_str = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, value_field));
value_str = Z_STRVAL_P(value_php_str);
value_len = Z_STRLEN_P(value_php_str);
@ -1355,17 +1410,21 @@ static void putjsonlistvalue(
upb_sink_startmsg(sink);
array = CACHED_PTR_TO_ZVAL_PTR(
DEREF(message_data(msg), offset, CACHED_VALUE*));
intern = UNBOX(RepeatedField, array);
ht = PHP_PROTO_HASH_OF(intern->array);
size = zend_hash_num_elements(ht);
if (size == 0) {
array = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f));
if (ZVAL_IS_NULL(array)) {
upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink);
upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ));
} else {
putarray(array, f, sink, depth, true TSRMLS_CC);
intern = UNBOX(RepeatedField, array);
ht = PHP_PROTO_HASH_OF(intern->array);
size = zend_hash_num_elements(ht);
if (size == 0) {
upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink);
upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ));
} else {
putarray(array, f, sink, depth, true TSRMLS_CC);
}
}
upb_sink_endmsg(sink, &status);
@ -1384,16 +1443,20 @@ static void putjsonstruct(
upb_sink_startmsg(sink);
map = CACHED_PTR_TO_ZVAL_PTR(
DEREF(message_data(msg), offset, CACHED_VALUE*));
intern = UNBOX(Map, map);
size = upb_strtable_count(&intern->table);
if (size == 0) {
map = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f));
if (ZVAL_IS_NULL(map)) {
upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink);
upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ));
} else {
putmap(map, f, sink, depth, true TSRMLS_CC);
intern = UNBOX(Map, map);
size = upb_strtable_count(&intern->table);
if (size == 0) {
upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink);
upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ));
} else {
putmap(map, f, sink, depth, true TSRMLS_CC);
}
}
upb_sink_endmsg(sink, &status);
@ -1455,28 +1518,24 @@ static void putrawmsg(MessageHeader* msg, const Descriptor* desc,
}
if (is_map_field(f)) {
zval* map = CACHED_PTR_TO_ZVAL_PTR(
DEREF(message_data(msg), offset, CACHED_VALUE*));
if (map != NULL) {
zval* map = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f));
if (!ZVAL_IS_NULL(map)) {
putmap(map, f, sink, depth, is_json TSRMLS_CC);
}
} else if (upb_fielddef_isseq(f)) {
zval* array = CACHED_PTR_TO_ZVAL_PTR(
DEREF(message_data(msg), offset, CACHED_VALUE*));
if (array != NULL) {
zval* array = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f));
if (!ZVAL_IS_NULL(array)) {
putarray(array, f, sink, depth, is_json TSRMLS_CC);
}
} else if (upb_fielddef_isstring(f)) {
zval* str = CACHED_PTR_TO_ZVAL_PTR(
DEREF(message_data(msg), offset, CACHED_VALUE*));
zval* str = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f));
if (containing_oneof || (is_json && is_wrapper_msg(desc->msgdef)) ||
Z_STRLEN_P(str) > 0) {
putstr(str, f, sink, is_json && is_wrapper_msg(desc->msgdef));
}
} else if (upb_fielddef_issubmsg(f)) {
putsubmsg(CACHED_PTR_TO_ZVAL_PTR(
DEREF(message_data(msg), offset, CACHED_VALUE*)),
f, sink, depth, is_json TSRMLS_CC);
zval* submsg = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f));
putsubmsg(submsg, f, sink, depth, is_json TSRMLS_CC);
} else {
upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f));
@ -1847,9 +1906,8 @@ static void discard_unknown_fields(MessageHeader* msg) {
value_field = map_field_value(f);
if (!upb_fielddef_issubmsg(value_field)) continue;
zval* map_php = CACHED_PTR_TO_ZVAL_PTR(
DEREF(message_data(msg), offset, CACHED_VALUE*));
if (map_php == NULL) continue;
zval* map_php = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f));
if (ZVAL_IS_NULL(map_php)) continue;
Map* intern = UNBOX(Map, map_php);
for (map_begin(map_php, &map_it TSRMLS_CC);
@ -1868,9 +1926,8 @@ static void discard_unknown_fields(MessageHeader* msg) {
} else if (upb_fielddef_isseq(f)) {
if (!upb_fielddef_issubmsg(f)) continue;
zval* array_php = CACHED_PTR_TO_ZVAL_PTR(
DEREF(message_data(msg), offset, CACHED_VALUE*));
if (array_php == NULL) continue;
zval* array_php = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f));
if (ZVAL_IS_NULL(array_php)) continue;
int size, i;
RepeatedField* intern = UNBOX(RepeatedField, array_php);
@ -1890,8 +1947,7 @@ static void discard_unknown_fields(MessageHeader* msg) {
discard_unknown_fields(submsg);
}
} else if (upb_fielddef_issubmsg(f)) {
zval* submsg_php = CACHED_PTR_TO_ZVAL_PTR(
DEREF(message_data(msg), offset, CACHED_VALUE*));
zval* submsg_php = CACHED_PTR_TO_ZVAL_PTR(find_zval_property(msg, f));
if (Z_TYPE_P(submsg_php) == IS_NULL) continue;
MessageHeader* submsg = UNBOX(MessageHeader, submsg_php);
discard_unknown_fields(submsg);

View File

@ -243,6 +243,18 @@ map_field_handlers->write_dimension = map_field_write_dimension;
map_field_handlers->get_gc = map_field_get_gc;
PHP_PROTO_INIT_CLASS_END
void map_field_ensure_created(const upb_fielddef *field,
CACHED_VALUE *map_field PHP_PROTO_TSRMLS_DC) {
if (ZVAL_IS_NULL(CACHED_PTR_TO_ZVAL_PTR(map_field))) {
zval_ptr_dtor(map_field);
#if PHP_MAJOR_VERSION < 7
MAKE_STD_ZVAL(CACHED_PTR_TO_ZVAL_PTR(map_field));
#endif
map_field_create_with_field(map_field_type, field,
map_field PHP_PROTO_TSRMLS_CC);
}
}
void map_field_create_with_field(const zend_class_entry *ce,
const upb_fielddef *field,
CACHED_VALUE *map_field PHP_PROTO_TSRMLS_DC) {

View File

@ -178,7 +178,7 @@ static zval* message_get_property_internal(zval* object,
zend_get_property_info(Z_OBJCE_P(object), Z_STR_P(member), true);
#endif
return layout_get(
self->descriptor->layout, message_data(self), field,
self->descriptor->layout, self, field,
OBJ_PROP(Z_OBJ_P(object), property_info->offset) TSRMLS_CC);
}
@ -191,7 +191,7 @@ static void message_get_oneof_property_internal(zval* object, zval* member,
return;
}
layout_get(self->descriptor->layout, message_data(self), field,
layout_get(self->descriptor->layout, self, field,
ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC);
}
@ -255,7 +255,6 @@ void custom_data_init(const zend_class_entry* ce,
MessageHeader* intern PHP_PROTO_TSRMLS_DC) {
Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_ce_obj(ce));
intern->data = ALLOC_N(uint8_t, desc->layout->size);
memset(message_data(intern), 0, desc->layout->size);
// We wrap first so that everything in the message object is GC-rooted in
// case a collection happens during object creation in layout_init().
intern->descriptor = desc;
@ -575,9 +574,9 @@ PHP_METHOD(Message, readOneof) {
const upb_fielddef* field = upb_msgdef_itof(msg->descriptor->msgdef, index);
// Unlike singular fields, oneof fields share cached property. So we cannot
// let lay_get modify the cached property. Instead, we pass in the return
// let layout_get modify the cached property. Instead, we pass in the return
// value directly.
layout_get(msg->descriptor->layout, message_data(msg), field,
layout_get(msg->descriptor->layout, msg, field,
ZVAL_PTR_TO_CACHED_PTR(return_value) TSRMLS_CC);
}

View File

@ -200,7 +200,7 @@
#define CACHED_VALUE zval*
#define CACHED_TO_ZVAL_PTR(VALUE) (VALUE)
#define CACHED_PTR_TO_ZVAL_PTR(VALUE) (*VALUE)
#define CACHED_PTR_TO_ZVAL_PTR(VALUE) (*(CACHED_VALUE*)(VALUE))
#define ZVAL_PTR_TO_CACHED_PTR(VALUE) (&VALUE)
#define ZVAL_PTR_TO_CACHED_VALUE(VALUE) (VALUE)
#define ZVAL_TO_CACHED_VALUE(VALUE) (&VALUE)
@ -475,7 +475,7 @@ static inline int php_proto_zend_hash_get_current_data_ex(HashTable* ht,
#define CACHED_VALUE zval
#define CACHED_TO_ZVAL_PTR(VALUE) (&VALUE)
#define CACHED_PTR_TO_ZVAL_PTR(VALUE) (VALUE)
#define CACHED_PTR_TO_ZVAL_PTR(VALUE) ((CACHED_VALUE*)(VALUE))
#define ZVAL_PTR_TO_CACHED_PTR(VALUE) (VALUE)
#define ZVAL_PTR_TO_CACHED_VALUE(VALUE) (*VALUE)
#define ZVAL_TO_CACHED_VALUE(VALUE) (VALUE)
@ -935,6 +935,7 @@ struct MessageField {
struct MessageLayout {
const upb_msgdef* msgdef;
void* empty_template; // Can memcpy() onto a layout to clear it.
MessageField* fields;
size_t size;
};
@ -948,7 +949,7 @@ PHP_PROTO_WRAP_OBJECT_END
MessageLayout* create_layout(const upb_msgdef* msgdef);
void layout_init(MessageLayout* layout, void* storage,
zend_object* object PHP_PROTO_TSRMLS_DC);
zval* layout_get(MessageLayout* layout, const void* storage,
zval* layout_get(MessageLayout* layout, MessageHeader* header,
const upb_fielddef* field, CACHED_VALUE* cache TSRMLS_DC);
void layout_set(MessageLayout* layout, MessageHeader* header,
const upb_fielddef* field, zval* val TSRMLS_DC);
@ -1089,6 +1090,8 @@ upb_value map_iter_value(MapIter* iter, int* len);
const upb_fielddef* map_entry_key(const upb_msgdef* msgdef);
const upb_fielddef* map_entry_value(const upb_msgdef* msgdef);
void map_field_ensure_created(const upb_fielddef *field,
CACHED_VALUE *map_field PHP_PROTO_TSRMLS_DC);
void map_field_create_with_field(const zend_class_entry* ce,
const upb_fielddef* field,
CACHED_VALUE* map_field PHP_PROTO_TSRMLS_DC);
@ -1147,6 +1150,9 @@ PHP_PROTO_WRAP_OBJECT_START(RepeatedFieldIter)
long position;
PHP_PROTO_WRAP_OBJECT_END
void repeated_field_ensure_created(
const upb_fielddef *field,
CACHED_VALUE *repeated_field PHP_PROTO_TSRMLS_DC);
void repeated_field_create_with_field(
zend_class_entry* ce, const upb_fielddef* field,
CACHED_VALUE* repeated_field PHP_PROTO_TSRMLS_DC);
@ -1489,6 +1495,9 @@ size_t stringsink_string(void *_sink, const void *hd, const char *ptr,
#define FREE(object) efree(object)
#define PEFREE(object) pefree(object, 1)
// Find corresponding zval property for the field.
CACHED_VALUE* find_zval_property(MessageHeader* msg, const upb_fielddef* field);
// String argument.
#define STR(str) (str), strlen(str)

View File

@ -75,11 +75,9 @@ static bool native_slot_is_default(upb_fieldtype_t type, const void* memory) {
#undef CASE_TYPE
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
return Z_STRLEN_P(CACHED_PTR_TO_ZVAL_PTR(DEREF(memory, CACHED_VALUE*))) ==
0;
return Z_STRLEN_P(CACHED_PTR_TO_ZVAL_PTR(memory)) == 0;
case UPB_TYPE_MESSAGE:
return Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(DEREF(memory, CACHED_VALUE*))) ==
IS_NULL;
return Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(memory)) == IS_NULL;
default: return false;
}
}
@ -599,6 +597,8 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) {
// Reserve space for unknown fields.
off += sizeof(void*);
layout->empty_template = NULL;
TSRMLS_FETCH();
Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msgdef));
layout->fields = ALLOC_N(MessageField, nfields);
@ -744,64 +744,35 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) {
layout->size = off;
layout->msgdef = msgdef;
// Create the empty message template.
layout->empty_template = ALLOC_N(char, layout->size);
memset(layout->empty_template, 0, layout->size);
return layout;
}
void free_layout(MessageLayout* layout) {
FREE(layout->empty_template);
FREE(layout->fields);
FREE(layout);
}
void layout_init(MessageLayout* layout, void* storage,
zend_object* object PHP_PROTO_TSRMLS_DC) {
int i;
upb_msg_field_iter it;
// Init unknown fields
memset(storage, 0, sizeof(void*));
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* memory = slot_memory(layout, storage, field);
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
int cache_index = slot_property_cache(layout, storage, field);
CACHED_VALUE* property_ptr = OBJ_PROP(object, cache_index);
if (upb_fielddef_containingoneof(field)) {
memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
*oneof_case = ONEOF_CASE_NONE;
} else if (is_map_field(field)) {
zval_ptr_dtor(property_ptr);
#if PHP_MAJOR_VERSION < 7
MAKE_STD_ZVAL(*property_ptr);
#endif
map_field_create_with_field(map_field_type, field,
property_ptr PHP_PROTO_TSRMLS_CC);
DEREF(memory, CACHED_VALUE*) = property_ptr;
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
zval_ptr_dtor(property_ptr);
#if PHP_MAJOR_VERSION < 7
MAKE_STD_ZVAL(*property_ptr);
#endif
repeated_field_create_with_field(repeated_field_type, field,
property_ptr PHP_PROTO_TSRMLS_CC);
DEREF(memory, CACHED_VALUE*) = property_ptr;
} else {
native_slot_init(upb_fielddef_type(field), memory, property_ptr);
}
}
memcpy(storage, layout->empty_template, layout->size);
}
// For non-singular fields, the related memory needs to point to the actual
// zval in properties table first.
static void* value_memory(const upb_fielddef* field, void* memory) {
switch (upb_fielddef_type(field)) {
// Switch memory for processing for singular fields based on field type.
// * primitive fields: memory
// * others (string, bytes and message): cache (the correspond zval
// property)
static void* value_memory(
upb_fieldtype_t type, void* memory, CACHED_VALUE* cache) {
switch (type) {
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
case UPB_TYPE_MESSAGE:
memory = DEREF(memory, CACHED_VALUE*);
break;
return cache;
default:
// No operation
break;
@ -809,8 +780,17 @@ static void* value_memory(const upb_fielddef* field, void* memory) {
return memory;
}
zval* layout_get(MessageLayout* layout, const void* storage,
CACHED_VALUE* find_zval_property(
MessageHeader* header, const upb_fielddef* field) {
int property_cache_index =
header->descriptor->layout->fields[upb_fielddef_index(field)]
.cache_index;
return OBJ_PROP(&header->std, property_cache_index);
}
zval* layout_get(MessageLayout* layout, MessageHeader* header,
const upb_fielddef* field, CACHED_VALUE* cache TSRMLS_DC) {
const void* storage = message_data(header);
void* memory = slot_memory(layout, storage, field);
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
@ -818,14 +798,21 @@ zval* layout_get(MessageLayout* layout, const void* storage,
if (*oneof_case != upb_fielddef_number(field)) {
native_slot_get_default(upb_fielddef_type(field), cache TSRMLS_CC);
} else {
native_slot_get(upb_fielddef_type(field), value_memory(field, memory),
cache TSRMLS_CC);
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);
} 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);
} else {
native_slot_get(upb_fielddef_type(field), value_memory(field, memory),
upb_fieldtype_t type = upb_fielddef_type(field);
native_slot_get(type, value_memory(type, memory, cache),
cache TSRMLS_CC);
return CACHED_PTR_TO_ZVAL_PTR(cache);
}
@ -868,8 +855,8 @@ void layout_set(MessageLayout* layout, MessageHeader* header,
*oneof_case = upb_fielddef_number(field);
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
// Works for both repeated and map fields
memory = DEREF(memory, void**);
zval* property_ptr = CACHED_PTR_TO_ZVAL_PTR((CACHED_VALUE*)memory);
CACHED_VALUE* cached = find_zval_property(header, field);
zval* property_ptr = CACHED_PTR_TO_ZVAL_PTR(cached);
if (EXPECTED(property_ptr != val)) {
zend_class_entry *subce = NULL;
@ -901,7 +888,7 @@ void layout_set(MessageLayout* layout, MessageHeader* header,
&converted_value);
}
#if PHP_MAJOR_VERSION < 7
REPLACE_ZVAL_VALUE((zval**)memory, &converted_value, 1);
REPLACE_ZVAL_VALUE((zval**)cached, &converted_value, 1);
#else
php_proto_zval_ptr_dtor(property_ptr);
ZVAL_ZVAL(property_ptr, &converted_value, 1, 0);
@ -916,12 +903,16 @@ void layout_set(MessageLayout* layout, MessageHeader* header,
Descriptor* desc = UNBOX_HASHTABLE_VALUE(Descriptor, get_def_obj(msg));
ce = desc->klass;
}
native_slot_set(type, ce, value_memory(field, memory), val TSRMLS_CC);
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(const upb_fielddef* field, const void* from_memory,
void* to_memory PHP_PROTO_TSRMLS_DC) {
static void native_slot_merge(
const upb_fielddef* field, const void* from_memory,
void* to_memory PHP_PROTO_TSRMLS_DC) {
upb_fieldtype_t type = upb_fielddef_type(field);
zend_class_entry* ce = NULL;
if (!native_slot_is_default(type, from_memory)) {
@ -943,9 +934,8 @@ static void native_slot_merge(const upb_fielddef* field, const void* from_memory
#undef CASE_TYPE
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
native_slot_set(type, NULL, value_memory(field, to_memory),
CACHED_PTR_TO_ZVAL_PTR(DEREF(
from_memory, CACHED_VALUE*)) PHP_PROTO_TSRMLS_CC);
native_slot_set(type, NULL, to_memory,
CACHED_PTR_TO_ZVAL_PTR(from_memory) PHP_PROTO_TSRMLS_CC);
break;
case UPB_TYPE_MESSAGE: {
const upb_msgdef* msg = upb_fielddef_msgsubdef(field);
@ -953,22 +943,21 @@ static void native_slot_merge(const upb_fielddef* field, const void* from_memory
ce = desc->klass;
if (native_slot_is_default(type, to_memory)) {
#if PHP_MAJOR_VERSION < 7
SEPARATE_ZVAL_IF_NOT_REF((zval**)value_memory(field, to_memory));
SEPARATE_ZVAL_IF_NOT_REF((zval**)to_memory);
#endif
CREATE_OBJ_ON_ALLOCATED_ZVAL_PTR(
CACHED_PTR_TO_ZVAL_PTR(DEREF(to_memory, CACHED_VALUE*)), ce);
CACHED_PTR_TO_ZVAL_PTR(to_memory), ce);
MessageHeader* submsg =
UNBOX(MessageHeader,
CACHED_PTR_TO_ZVAL_PTR(DEREF(to_memory, CACHED_VALUE*)));
UNBOX(MessageHeader, CACHED_PTR_TO_ZVAL_PTR(to_memory));
custom_data_init(ce, submsg PHP_PROTO_TSRMLS_CC);
}
MessageHeader* sub_from =
UNBOX(MessageHeader,
CACHED_PTR_TO_ZVAL_PTR(DEREF(from_memory, CACHED_VALUE*)));
CACHED_PTR_TO_ZVAL_PTR(from_memory));
MessageHeader* sub_to =
UNBOX(MessageHeader,
CACHED_PTR_TO_ZVAL_PTR(DEREF(to_memory, CACHED_VALUE*)));
CACHED_PTR_TO_ZVAL_PTR(to_memory));
layout_merge(desc->layout, sub_from, sub_to PHP_PROTO_TSRMLS_CC);
break;
@ -1069,10 +1058,17 @@ void layout_merge(MessageLayout* layout, MessageHeader* from,
int size, key_length, value_length;
MapIter map_it;
zval* to_map_php =
CACHED_PTR_TO_ZVAL_PTR(DEREF(to_memory, CACHED_VALUE*));
zval* from_map_php =
CACHED_PTR_TO_ZVAL_PTR(DEREF(from_memory, CACHED_VALUE*));
CACHED_VALUE* from_cache = find_zval_property(from, field);
CACHED_VALUE* to_cache = find_zval_property(to, field);
if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(from_cache)) == IS_NULL) {
continue;
}
map_field_ensure_created(field, to_cache PHP_PROTO_TSRMLS_CC);
zval* to_map_php = CACHED_PTR_TO_ZVAL_PTR(to_cache);
zval* from_map_php = CACHED_PTR_TO_ZVAL_PTR(from_cache);
Map* to_map = UNBOX(Map, to_map_php);
Map* from_map = UNBOX(Map, from_map_php);
@ -1098,8 +1094,16 @@ void layout_merge(MessageLayout* layout, MessageHeader* from,
}
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
zval* to_array_php = CACHED_PTR_TO_ZVAL_PTR(DEREF(to_memory, CACHED_VALUE*));
zval* from_array_php = CACHED_PTR_TO_ZVAL_PTR(DEREF(from_memory, CACHED_VALUE*));
CACHED_VALUE* from_cache = find_zval_property(from, field);
CACHED_VALUE* to_cache = find_zval_property(to, field);
if (Z_TYPE_P(CACHED_PTR_TO_ZVAL_PTR(from_cache)) == IS_NULL) {
continue;
}
repeated_field_ensure_created(field, to_cache PHP_PROTO_TSRMLS_CC);
zval* to_array_php = CACHED_PTR_TO_ZVAL_PTR(to_cache);
zval* from_array_php = CACHED_PTR_TO_ZVAL_PTR(from_cache);
RepeatedField* to_array = UNBOX(RepeatedField, to_array_php);
RepeatedField* from_array = UNBOX(RepeatedField, from_array_php);
@ -1129,7 +1133,19 @@ void layout_merge(MessageLayout* layout, MessageHeader* from,
}
}
} else {
native_slot_merge(field, from_memory, to_memory PHP_PROTO_TSRMLS_CC);
switch (upb_fielddef_type(field)) {
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
case UPB_TYPE_MESSAGE: {
CACHED_VALUE* from_cache = find_zval_property(from, field);
CACHED_VALUE* to_cache = find_zval_property(to, field);
native_slot_merge(field, from_cache, to_cache PHP_PROTO_TSRMLS_CC);
break;
}
default:
native_slot_merge(field, from_memory, to_memory PHP_PROTO_TSRMLS_CC);
break;
}
}
}
}