Index: third_party/protobuf/php/ext/google/protobuf/encode_decode.c |
diff --git a/third_party/protobuf/ruby/ext/google/protobuf_c/encode_decode.c b/third_party/protobuf/php/ext/google/protobuf/encode_decode.c |
similarity index 59% |
copy from third_party/protobuf/ruby/ext/google/protobuf_c/encode_decode.c |
copy to third_party/protobuf/php/ext/google/protobuf/encode_decode.c |
index f6bea50f3966652a93f0ce56b133fa4cd73633ab..eafe1ae8328c977c4ab54549119af66954035afb 100644 |
--- a/third_party/protobuf/ruby/ext/google/protobuf_c/encode_decode.c |
+++ b/third_party/protobuf/php/ext/google/protobuf/encode_decode.c |
@@ -1,5 +1,5 @@ |
// Protocol Buffers - Google's data interchange format |
-// Copyright 2014 Google Inc. All rights reserved. |
+// Copyright 2008 Google Inc. All rights reserved. |
// https://developers.google.com/protocol-buffers/ |
// |
// Redistribution and use in source and binary forms, with or without |
@@ -29,19 +29,100 @@ |
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
#include "protobuf.h" |
+#include "utf8.h" |
-// This function is equivalent to rb_str_cat(), but unlike the real |
-// rb_str_cat(), it doesn't leak memory in some versions of Ruby. |
-// For more information, see: |
-// https://bugs.ruby-lang.org/issues/11328 |
-VALUE noleak_rb_str_cat(VALUE rb_str, const char *str, long len) { |
- char *p; |
- size_t oldlen = RSTRING_LEN(rb_str); |
- rb_str_modify_expand(rb_str, len); |
- p = RSTRING_PTR(rb_str); |
- memcpy(p + oldlen, str, len); |
- rb_str_set_len(rb_str, oldlen + len); |
- return rb_str; |
+/* stringsink *****************************************************************/ |
+ |
+typedef struct { |
+ upb_byteshandler handler; |
+ upb_bytessink sink; |
+ char *ptr; |
+ size_t len, size; |
+} stringsink; |
+ |
+ |
+static void *stringsink_start(void *_sink, const void *hd, size_t size_hint) { |
+ stringsink *sink = _sink; |
+ sink->len = 0; |
+ return sink; |
+} |
+ |
+static size_t stringsink_string(void *_sink, const void *hd, const char *ptr, |
+ size_t len, const upb_bufhandle *handle) { |
+ stringsink *sink = _sink; |
+ size_t new_size = sink->size; |
+ |
+ UPB_UNUSED(hd); |
+ UPB_UNUSED(handle); |
+ |
+ while (sink->len + len > new_size) { |
+ new_size *= 2; |
+ } |
+ |
+ if (new_size != sink->size) { |
+ sink->ptr = realloc(sink->ptr, new_size); |
+ sink->size = new_size; |
+ } |
+ |
+ memcpy(sink->ptr + sink->len, ptr, len); |
+ sink->len += len; |
+ |
+ return len; |
+} |
+ |
+void stringsink_init(stringsink *sink) { |
+ upb_byteshandler_init(&sink->handler); |
+ upb_byteshandler_setstartstr(&sink->handler, stringsink_start, NULL); |
+ upb_byteshandler_setstring(&sink->handler, stringsink_string, NULL); |
+ |
+ upb_bytessink_reset(&sink->sink, &sink->handler, sink); |
+ |
+ sink->size = 32; |
+ sink->ptr = malloc(sink->size); |
+ sink->len = 0; |
+} |
+ |
+void stringsink_uninit(stringsink *sink) { free(sink->ptr); } |
+ |
+/* stackenv *****************************************************************/ |
+ |
+// Stack-allocated context during an encode/decode operation. Contains the upb |
+// environment and its stack-based allocator, an initial buffer for allocations |
+// to avoid malloc() when possible, and a template for PHP exception messages |
+// if any error occurs. |
+#define STACK_ENV_STACKBYTES 4096 |
+typedef struct { |
+ upb_env env; |
+ const char *php_error_template; |
+ char allocbuf[STACK_ENV_STACKBYTES]; |
+} stackenv; |
+ |
+ |
+static void stackenv_init(stackenv* se, const char* errmsg); |
+static void stackenv_uninit(stackenv* se); |
+ |
+// Callback invoked by upb if any error occurs during parsing or serialization. |
+static bool env_error_func(void* ud, const upb_status* status) { |
+ stackenv* se = ud; |
+ // Free the env -- zend_error will longjmp up the stack past the |
+ // encode/decode function so it would not otherwise have been freed. |
+ stackenv_uninit(se); |
+ |
+ // TODO(teboring): have a way to verify that this is actually a parse error, |
+ // instead of just throwing "parse error" unconditionally. |
+ zend_error(E_ERROR, se->php_error_template, upb_status_errmsg(status)); |
+ // Never reached. |
+ return false; |
+} |
+ |
+static void stackenv_init(stackenv* se, const char* errmsg) { |
+ se->php_error_template = errmsg; |
+ upb_env_init2(&se->env, se->allocbuf, sizeof(se->allocbuf), NULL); |
+ upb_env_seterrorfunc(&se->env, env_error_func, se); |
+} |
+ |
+static void stackenv_uninit(stackenv* se) { |
+ upb_env_uninit(&se->env); |
} |
// ----------------------------------------------------------------------------- |
@@ -52,7 +133,7 @@ VALUE noleak_rb_str_cat(VALUE rb_str, const char *str, long len) { |
// Creates a handlerdata that simply contains the offset for this field. |
static const void* newhandlerdata(upb_handlers* h, uint32_t ofs) { |
- size_t* hd_ofs = ALLOC(size_t); |
+ size_t* hd_ofs = (size_t*)malloc(sizeof(size_t)); |
*hd_ofs = ofs; |
upb_handlers_addcleanup(h, hd_ofs, free); |
return hd_ofs; |
@@ -66,7 +147,8 @@ typedef struct { |
// Creates a handlerdata that contains offset and submessage type information. |
static const void *newsubmsghandlerdata(upb_handlers* h, uint32_t ofs, |
const upb_fielddef* f) { |
- submsg_handlerdata_t *hd = ALLOC(submsg_handlerdata_t); |
+ submsg_handlerdata_t* hd = |
+ (submsg_handlerdata_t*)malloc(sizeof(submsg_handlerdata_t)); |
hd->ofs = ofs; |
hd->md = upb_fielddef_msgsubdef(f); |
upb_handlers_addcleanup(h, hd, free); |
@@ -76,6 +158,7 @@ static const void *newsubmsghandlerdata(upb_handlers* h, uint32_t ofs, |
typedef struct { |
size_t ofs; // union data slot |
size_t case_ofs; // oneof_case field |
+ int property_ofs; // properties table cache |
uint32_t oneof_case_num; // oneof-case number to place in oneof_case field |
const upb_msgdef *md; // msgdef, for oneof submessage handler |
} oneof_handlerdata_t; |
@@ -83,10 +166,13 @@ typedef struct { |
static const void *newoneofhandlerdata(upb_handlers *h, |
uint32_t ofs, |
uint32_t case_ofs, |
+ int property_ofs, |
const upb_fielddef *f) { |
- oneof_handlerdata_t *hd = ALLOC(oneof_handlerdata_t); |
+ oneof_handlerdata_t* hd = |
+ (oneof_handlerdata_t*)malloc(sizeof(oneof_handlerdata_t)); |
hd->ofs = ofs; |
hd->case_ofs = case_ofs; |
+ hd->property_ofs = property_ofs; |
// We reuse the field tag number as a oneof union discriminant tag. Note that |
// we don't expose these numbers to the user, so the only requirement is that |
// we have some unique ID for each union case/possibility. The field tag |
@@ -108,16 +194,19 @@ static const void *newoneofhandlerdata(upb_handlers *h, |
static void *startseq_handler(void* closure, const void* hd) { |
MessageHeader* msg = closure; |
const size_t *ofs = hd; |
- return (void*)DEREF(msg, *ofs, VALUE); |
+ return (void*)(*DEREF(msg, *ofs, zval**)); |
} |
// Handlers that append primitive values to a repeated field. |
-#define DEFINE_APPEND_HANDLER(type, ctype) \ |
- static bool append##type##_handler(void *closure, const void *hd, \ |
- ctype val) { \ |
- VALUE ary = (VALUE)closure; \ |
- RepeatedField_push_native(ary, &val); \ |
- return true; \ |
+#define DEFINE_APPEND_HANDLER(type, ctype) \ |
+ static bool append##type##_handler(void* closure, const void* hd, \ |
+ ctype val) { \ |
+ zval* array = (zval*)closure; \ |
+ TSRMLS_FETCH(); \ |
+ RepeatedField* intern = \ |
+ (RepeatedField*)zend_object_store_get_object(array TSRMLS_CC); \ |
+ repeated_field_push_native(intern, &val TSRMLS_CC); \ |
+ return true; \ |
} |
DEFINE_APPEND_HANDLER(bool, bool) |
@@ -132,10 +221,16 @@ DEFINE_APPEND_HANDLER(double, double) |
static void* appendstr_handler(void *closure, |
const void *hd, |
size_t size_hint) { |
- VALUE ary = (VALUE)closure; |
- VALUE str = rb_str_new2(""); |
- rb_enc_associate(str, kRubyStringUtf8Encoding); |
- RepeatedField_push(ary, str); |
+ zval* array = (zval*)closure; |
+ TSRMLS_FETCH(); |
+ RepeatedField* intern = |
+ (RepeatedField*)zend_object_store_get_object(array TSRMLS_CC); |
+ |
+ zval* str; |
+ MAKE_STD_ZVAL(str); |
+ ZVAL_STRING(str, "", 1); |
+ |
+ repeated_field_push_native(intern, &str TSRMLS_CC); |
return (void*)str; |
} |
@@ -143,10 +238,24 @@ static void* appendstr_handler(void *closure, |
static void* appendbytes_handler(void *closure, |
const void *hd, |
size_t size_hint) { |
- VALUE ary = (VALUE)closure; |
- VALUE str = rb_str_new2(""); |
- rb_enc_associate(str, kRubyString8bitEncoding); |
- RepeatedField_push(ary, str); |
+ zval* array = (zval*)closure; |
+ TSRMLS_FETCH(); |
+ RepeatedField* intern = |
+ (RepeatedField*)zend_object_store_get_object(array TSRMLS_CC); |
+ |
+ zval* str; |
+ MAKE_STD_ZVAL(str); |
+ ZVAL_STRING(str, "", 1); |
+ |
+ repeated_field_push_native(intern, &str TSRMLS_CC); |
+ return (void*)str; |
+} |
+ |
+static void *empty_php_string(zval** value_ptr) { |
+ SEPARATE_ZVAL_IF_NOT_REF(value_ptr); |
+ zval* str = *value_ptr; |
+ zval_dtor(str); |
+ ZVAL_STRINGL(str, "", 0, 1); |
return (void*)str; |
} |
@@ -156,10 +265,7 @@ static void* str_handler(void *closure, |
size_t size_hint) { |
MessageHeader* msg = closure; |
const size_t *ofs = hd; |
- VALUE str = rb_str_new2(""); |
- rb_enc_associate(str, kRubyStringUtf8Encoding); |
- DEREF(msg, *ofs, VALUE) = str; |
- return (void*)str; |
+ return empty_php_string(DEREF(msg, *ofs, zval**)); |
} |
// Sets a non-repeated 'bytes' field in a message. |
@@ -168,33 +274,52 @@ static void* bytes_handler(void *closure, |
size_t size_hint) { |
MessageHeader* msg = closure; |
const size_t *ofs = hd; |
- VALUE str = rb_str_new2(""); |
- rb_enc_associate(str, kRubyString8bitEncoding); |
- DEREF(msg, *ofs, VALUE) = str; |
- return (void*)str; |
+ return empty_php_string(DEREF(msg, *ofs, zval**)); |
} |
static size_t stringdata_handler(void* closure, const void* hd, |
const char* str, size_t len, |
const upb_bufhandle* handle) { |
- VALUE rb_str = (VALUE)closure; |
- noleak_rb_str_cat(rb_str, str, len); |
+ zval* php_str = (zval*)closure; |
+ |
+ char* old_str = Z_STRVAL_P(php_str); |
+ size_t old_len = Z_STRLEN_P(php_str); |
+ assert(old_str != NULL); |
+ |
+ char* new_str = emalloc(old_len + len + 1); |
+ |
+ memcpy(new_str, old_str, old_len); |
+ memcpy(new_str + old_len, str, len); |
+ new_str[old_len + len] = 0; |
+ FREE(old_str); |
+ |
+ Z_STRVAL_P(php_str) = new_str; |
+ Z_STRLEN_P(php_str) = old_len + len; |
+ |
return len; |
} |
-// Appends a submessage to a repeated field (a regular Ruby array for now). |
+// Appends a submessage to a repeated field. |
static void *appendsubmsg_handler(void *closure, const void *hd) { |
- VALUE ary = (VALUE)closure; |
+ zval* array = (zval*)closure; |
+ TSRMLS_FETCH(); |
+ RepeatedField* intern = |
+ (RepeatedField*)zend_object_store_get_object(array TSRMLS_CC); |
+ |
const submsg_handlerdata_t *submsgdata = hd; |
- VALUE subdesc = |
- get_def_obj((void*)submsgdata->md); |
- VALUE subklass = Descriptor_msgclass(subdesc); |
+ zval* subdesc_php = get_def_obj((void*)submsgdata->md); |
+ Descriptor* subdesc = zend_object_store_get_object(subdesc_php TSRMLS_CC); |
+ zend_class_entry* subklass = subdesc->klass; |
MessageHeader* submsg; |
- VALUE submsg_rb = rb_class_new_instance(0, NULL, subklass); |
- RepeatedField_push(ary, submsg_rb); |
+ zval* val = NULL; |
+ MAKE_STD_ZVAL(val); |
+ Z_TYPE_P(val) = IS_OBJECT; |
+ Z_OBJVAL_P(val) = subklass->create_object(subklass TSRMLS_CC); |
+ |
+ repeated_field_push_native(intern, &val TSRMLS_CC); |
- TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg); |
+ submsg = zend_object_store_get_object(val TSRMLS_CC); |
return submsg; |
} |
@@ -202,19 +327,26 @@ static void *appendsubmsg_handler(void *closure, const void *hd) { |
static void *submsg_handler(void *closure, const void *hd) { |
MessageHeader* msg = closure; |
const submsg_handlerdata_t* submsgdata = hd; |
- VALUE subdesc = |
- get_def_obj((void*)submsgdata->md); |
- VALUE subklass = Descriptor_msgclass(subdesc); |
- VALUE submsg_rb; |
+ zval* subdesc_php = get_def_obj((void*)submsgdata->md); |
+ TSRMLS_FETCH(); |
+ Descriptor* subdesc = zend_object_store_get_object(subdesc_php TSRMLS_CC); |
+ zend_class_entry* subklass = subdesc->klass; |
+ zval* submsg_php; |
MessageHeader* submsg; |
- if (DEREF(msg, submsgdata->ofs, VALUE) == Qnil) { |
- DEREF(msg, submsgdata->ofs, VALUE) = |
- rb_class_new_instance(0, NULL, subklass); |
+ if (Z_TYPE_P(*DEREF(msg, submsgdata->ofs, zval**)) == IS_NULL) { |
+ zval* val = NULL; |
+ MAKE_STD_ZVAL(val); |
+ Z_TYPE_P(val) = IS_OBJECT; |
+ Z_OBJVAL_P(val) = subklass->create_object(subklass TSRMLS_CC); |
+ |
+ zval_ptr_dtor(DEREF(msg, submsgdata->ofs, zval**)); |
+ *DEREF(msg, submsgdata->ofs, zval**) = val; |
} |
- submsg_rb = DEREF(msg, submsgdata->ofs, VALUE); |
- TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg); |
+ submsg_php = *DEREF(msg, submsgdata->ofs, zval**); |
+ |
+ submsg = zend_object_store_get_object(submsg_php TSRMLS_CC); |
return submsg; |
} |
@@ -237,51 +369,128 @@ typedef struct { |
// submessage. When the submessage ends, another handler is called to insert the |
// value into the map. |
typedef struct { |
- VALUE map; |
+ zval* map; |
char key_storage[NATIVE_SLOT_MAX_SIZE]; |
char value_storage[NATIVE_SLOT_MAX_SIZE]; |
} map_parse_frame_t; |
+static void map_slot_init(void* memory, upb_fieldtype_t type) { |
+ switch (type) { |
+ case UPB_TYPE_STRING: |
+ case UPB_TYPE_BYTES: { |
+ // Store zval** in memory in order to be consistent with the layout of |
+ // singular fields. |
+ zval** holder = ALLOC(zval*); |
+ zval* tmp; |
+ MAKE_STD_ZVAL(tmp); |
+ ZVAL_STRINGL(tmp, "", 0, 1); |
+ *holder = tmp; |
+ *(zval***)memory = holder; |
+ break; |
+ } |
+ case UPB_TYPE_MESSAGE: { |
+ zval** holder = ALLOC(zval*); |
+ zval* tmp; |
+ MAKE_STD_ZVAL(tmp); |
+ ZVAL_NULL(tmp); |
+ *holder = tmp; |
+ *(zval***)memory = holder; |
+ break; |
+ } |
+ default: |
+ native_slot_init(type, memory, NULL); |
+ } |
+} |
+ |
+static void map_slot_uninit(void* memory, upb_fieldtype_t type) { |
+ switch (type) { |
+ case UPB_TYPE_MESSAGE: |
+ case UPB_TYPE_STRING: |
+ case UPB_TYPE_BYTES: { |
+ zval** holder = *(zval***)memory; |
+ zval_ptr_dtor(holder); |
+ FREE(holder); |
+ break; |
+ } |
+ default: |
+ break; |
+ } |
+} |
+ |
+static void map_slot_key(upb_fieldtype_t type, const void* from, |
+ const char** keyval, |
+ size_t* length) { |
+ if (type == UPB_TYPE_STRING) { |
+ zval* key_php = **(zval***)from; |
+ *keyval = Z_STRVAL_P(key_php); |
+ *length = Z_STRLEN_P(key_php); |
+ } else { |
+ *keyval = from; |
+ *length = native_slot_size(type); |
+ } |
+} |
+ |
+static void map_slot_value(upb_fieldtype_t type, const void* from, |
+ upb_value* v) { |
+ size_t len; |
+ void* to = upb_value_memory(v); |
+#ifndef NDEBUG |
+ v->ctype = UPB_CTYPE_UINT64; |
+#endif |
+ |
+ memset(to, 0, native_slot_size(type)); |
+ |
+ switch (type) { |
+ case UPB_TYPE_STRING: |
+ case UPB_TYPE_BYTES: |
+ case UPB_TYPE_MESSAGE: { |
+ *(zval**)to = **(zval***)from; |
+ Z_ADDREF_PP((zval**)to); |
+ break; |
+ } |
+ default: |
+ len = native_slot_size(type); |
+ memcpy(to, from, len); |
+ } |
+} |
+ |
// Handler to begin a map entry: allocates a temporary frame. This is the |
// 'startsubmsg' handler on the msgdef that contains the map field. |
static void *startmapentry_handler(void *closure, const void *hd) { |
MessageHeader* msg = closure; |
const map_handlerdata_t* mapdata = hd; |
- VALUE map_rb = DEREF(msg, mapdata->ofs, VALUE); |
+ zval* map = *DEREF(msg, mapdata->ofs, zval**); |
map_parse_frame_t* frame = ALLOC(map_parse_frame_t); |
- frame->map = map_rb; |
+ frame->map = map; |
- native_slot_init(mapdata->key_field_type, &frame->key_storage); |
- native_slot_init(mapdata->value_field_type, &frame->value_storage); |
+ map_slot_init(&frame->key_storage, mapdata->key_field_type); |
+ map_slot_init(&frame->value_storage, mapdata->value_field_type); |
return frame; |
} |
// Handler to end a map entry: inserts the value defined during the message into |
// the map. This is the 'endmsg' handler on the map entry msgdef. |
-static bool endmap_handler(void *closure, const void *hd, upb_status* s) { |
+static bool endmap_handler(void* closure, const void* hd, upb_status* s) { |
map_parse_frame_t* frame = closure; |
const map_handlerdata_t* mapdata = hd; |
- VALUE key = native_slot_get( |
- mapdata->key_field_type, Qnil, |
- &frame->key_storage); |
+ TSRMLS_FETCH(); |
+ Map *map = (Map *)zend_object_store_get_object(frame->map TSRMLS_CC); |
- VALUE value_field_typeclass = Qnil; |
- VALUE value; |
+ const char* keyval = NULL; |
+ upb_value v; |
+ size_t length; |
- if (mapdata->value_field_type == UPB_TYPE_MESSAGE || |
- mapdata->value_field_type == UPB_TYPE_ENUM) { |
- value_field_typeclass = get_def_obj(mapdata->value_field_subdef); |
- } |
+ map_slot_key(map->key_type, &frame->key_storage, &keyval, &length); |
+ map_slot_value(map->value_type, &frame->value_storage, &v); |
- value = native_slot_get( |
- mapdata->value_field_type, value_field_typeclass, |
- &frame->value_storage); |
+ map_index_set(map, keyval, length, v); |
- Map_index_set(frame->map, key, value); |
- free(frame); |
+ map_slot_uninit(&frame->key_storage, mapdata->key_field_type); |
+ map_slot_uninit(&frame->value_storage, mapdata->value_field_type); |
+ FREE(frame); |
return true; |
} |
@@ -299,7 +508,10 @@ static map_handlerdata_t* new_map_handlerdata( |
Descriptor* desc) { |
const upb_fielddef* key_field; |
const upb_fielddef* value_field; |
- map_handlerdata_t* hd = ALLOC(map_handlerdata_t); |
+ // TODO(teboring): Use emalloc and efree. |
+ map_handlerdata_t* hd = |
+ (map_handlerdata_t*)malloc(sizeof(map_handlerdata_t)); |
+ |
hd->ofs = ofs; |
key_field = upb_msgdef_itof(mapentry_def, MAP_KEY_FIELD); |
assert(key_field != NULL); |
@@ -339,12 +551,13 @@ static void *oneofstr_handler(void *closure, |
size_t size_hint) { |
MessageHeader* msg = closure; |
const oneof_handlerdata_t *oneofdata = hd; |
- VALUE str = rb_str_new2(""); |
- rb_enc_associate(str, kRubyStringUtf8Encoding); |
+ |
DEREF(msg, oneofdata->case_ofs, uint32_t) = |
oneofdata->oneof_case_num; |
- DEREF(msg, oneofdata->ofs, VALUE) = str; |
- return (void*)str; |
+ DEREF(msg, oneofdata->ofs, zval**) = |
+ &(msg->std.properties_table)[oneofdata->property_ofs]; |
+ |
+ return empty_php_string(DEREF(msg, oneofdata->ofs, zval**)); |
} |
static void *oneofbytes_handler(void *closure, |
@@ -352,43 +565,54 @@ static void *oneofbytes_handler(void *closure, |
size_t size_hint) { |
MessageHeader* msg = closure; |
const oneof_handlerdata_t *oneofdata = hd; |
- VALUE str = rb_str_new2(""); |
- rb_enc_associate(str, kRubyString8bitEncoding); |
+ |
DEREF(msg, oneofdata->case_ofs, uint32_t) = |
oneofdata->oneof_case_num; |
- DEREF(msg, oneofdata->ofs, VALUE) = str; |
+ DEREF(msg, oneofdata->ofs, zval**) = |
+ &(msg->std.properties_table)[oneofdata->property_ofs]; |
+ |
+ // TODO(teboring): Add it back. |
+ // rb_enc_associate(str, kRubyString8bitEncoding); |
+ |
+ SEPARATE_ZVAL_IF_NOT_REF(DEREF(msg, oneofdata->ofs, zval**)); |
+ zval* str = *DEREF(msg, oneofdata->ofs, zval**); |
+ zval_dtor(str); |
+ ZVAL_STRINGL(str, "", 0, 1); |
return (void*)str; |
} |
// Handler for a submessage field in a oneof. |
-static void *oneofsubmsg_handler(void *closure, |
- const void *hd) { |
+static void* oneofsubmsg_handler(void* closure, const void* hd) { |
MessageHeader* msg = closure; |
const oneof_handlerdata_t *oneofdata = hd; |
uint32_t oldcase = DEREF(msg, oneofdata->case_ofs, uint32_t); |
- |
- VALUE subdesc = |
- get_def_obj((void*)oneofdata->md); |
- VALUE subklass = Descriptor_msgclass(subdesc); |
- VALUE submsg_rb; |
+ zval* subdesc_php = get_def_obj((void*)oneofdata->md); |
+ TSRMLS_FETCH(); |
+ Descriptor* subdesc = zend_object_store_get_object(subdesc_php TSRMLS_CC); |
+ zend_class_entry* subklass = subdesc->klass; |
+ zval* submsg_php; |
MessageHeader* submsg; |
- if (oldcase != oneofdata->oneof_case_num || |
- DEREF(msg, oneofdata->ofs, VALUE) == Qnil) { |
- DEREF(msg, oneofdata->ofs, VALUE) = |
- rb_class_new_instance(0, NULL, subklass); |
+ if (oldcase != oneofdata->oneof_case_num) { |
+ DEREF(msg, oneofdata->ofs, zval**) = |
+ &(msg->std.properties_table)[oneofdata->property_ofs]; |
+ } |
+ |
+ if (Z_TYPE_P(*DEREF(msg, oneofdata->ofs, zval**)) == IS_NULL) { |
+ zval* val = NULL; |
+ MAKE_STD_ZVAL(val); |
+ Z_TYPE_P(val) = IS_OBJECT; |
+ Z_OBJVAL_P(val) = subklass->create_object(subklass TSRMLS_CC); |
+ |
+ zval_ptr_dtor(DEREF(msg, oneofdata->ofs, zval**)); |
+ *DEREF(msg, oneofdata->ofs, zval**) = val; |
} |
- // Set the oneof case *after* allocating the new class instance -- otherwise, |
- // if the Ruby GC is invoked as part of a call into the VM, it might invoke |
- // our mark routines, and our mark routines might see the case value |
- // indicating a VALUE is present and expect a valid VALUE. See comment in |
- // layout_set() for more detail: basically, the change to the value and the |
- // case must be atomic w.r.t. the Ruby VM. |
+ |
DEREF(msg, oneofdata->case_ofs, uint32_t) = |
oneofdata->oneof_case_num; |
- submsg_rb = DEREF(msg, oneofdata->ofs, VALUE); |
- TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg); |
+ submsg_php = *DEREF(msg, oneofdata->ofs, zval**); |
+ submsg = zend_object_store_get_object(submsg_php TSRMLS_CC); |
return submsg; |
} |
@@ -491,8 +715,7 @@ static void add_handlers_for_mapfield(upb_handlers* h, |
} |
// Adds handlers to a map-entry msgdef. |
-static void add_handlers_for_mapentry(const upb_msgdef* msgdef, |
- upb_handlers* h, |
+static void add_handlers_for_mapentry(const upb_msgdef* msgdef, upb_handlers* h, |
Descriptor* desc) { |
const upb_fielddef* key_field = map_entry_key(msgdef); |
const upb_fielddef* value_field = map_entry_value(msgdef); |
@@ -503,23 +726,23 @@ static void add_handlers_for_mapentry(const upb_msgdef* msgdef, |
upb_handlerattr_sethandlerdata(&attr, hd); |
upb_handlers_setendmsg(h, endmap_handler, &attr); |
- add_handlers_for_singular_field( |
- h, key_field, |
- offsetof(map_parse_frame_t, key_storage)); |
- add_handlers_for_singular_field( |
- h, value_field, |
- offsetof(map_parse_frame_t, value_storage)); |
+ add_handlers_for_singular_field(h, key_field, |
+ offsetof(map_parse_frame_t, key_storage)); |
+ add_handlers_for_singular_field(h, value_field, |
+ offsetof(map_parse_frame_t, value_storage)); |
} |
// Set up handlers for a oneof field. |
static void add_handlers_for_oneof_field(upb_handlers *h, |
const upb_fielddef *f, |
size_t offset, |
- size_t oneof_case_offset) { |
+ size_t oneof_case_offset, |
+ int property_cache_offset) { |
upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER; |
upb_handlerattr_sethandlerdata( |
- &attr, newoneofhandlerdata(h, offset, oneof_case_offset, f)); |
+ &attr, newoneofhandlerdata(h, offset, oneof_case_offset, |
+ property_cache_offset, f)); |
switch (upb_fielddef_type(f)) { |
@@ -557,10 +780,12 @@ static void add_handlers_for_oneof_field(upb_handlers *h, |
upb_handlerattr_uninit(&attr); |
} |
- |
-static void add_handlers_for_message(const void *closure, upb_handlers *h) { |
+static void add_handlers_for_message(const void* closure, |
+ upb_handlers* h) { |
const upb_msgdef* msgdef = upb_handlers_msgdef(h); |
- Descriptor* desc = ruby_to_Descriptor(get_def_obj((void*)msgdef)); |
+ TSRMLS_FETCH(); |
+ Descriptor* desc = (Descriptor*)zend_object_store_get_object( |
+ get_def_obj((void*)msgdef) TSRMLS_CC); |
upb_msg_field_iter i; |
// If this is a mapentry message type, set up a special set of handlers and |
@@ -589,7 +814,10 @@ static void add_handlers_for_message(const void *closure, upb_handlers *h) { |
size_t oneof_case_offset = |
desc->layout->fields[upb_fielddef_index(f)].case_offset + |
sizeof(MessageHeader); |
- add_handlers_for_oneof_field(h, f, offset, oneof_case_offset); |
+ int property_cache_index = |
+ desc->layout->fields[upb_fielddef_index(f)].cache_index; |
+ add_handlers_for_oneof_field(h, f, offset, oneof_case_offset, |
+ property_cache_index); |
} else if (is_map_field(f)) { |
add_handlers_for_mapfield(h, f, offset, desc); |
} else if (upb_fielddef_isseq(f)) { |
@@ -621,8 +849,6 @@ const upb_handlers* get_fill_handlers(Descriptor* desc) { |
return desc->fill_handlers; |
} |
-// Constructs the upb decoder method for parsing messages of this type. |
-// This is called from the message class creation code. |
const upb_pbdecodermethod *new_fillmsg_decodermethod(Descriptor* desc, |
const void* owner) { |
const upb_handlers* handlers = get_fill_handlers(desc); |
@@ -640,362 +866,105 @@ static const upb_pbdecodermethod *msgdef_decodermethod(Descriptor* desc) { |
return desc->fill_method; |
} |
-static const upb_json_parsermethod *msgdef_jsonparsermethod(Descriptor* desc) { |
- if (desc->json_fill_method == NULL) { |
- desc->json_fill_method = |
- upb_json_parsermethod_new(desc->msgdef, &desc->json_fill_method); |
- } |
- return desc->json_fill_method; |
-} |
- |
- |
-// Stack-allocated context during an encode/decode operation. Contains the upb |
-// environment and its stack-based allocator, an initial buffer for allocations |
-// to avoid malloc() when possible, and a template for Ruby exception messages |
-// if any error occurs. |
-#define STACK_ENV_STACKBYTES 4096 |
-typedef struct { |
- upb_env env; |
- const char* ruby_error_template; |
- char allocbuf[STACK_ENV_STACKBYTES]; |
-} stackenv; |
- |
-static void stackenv_init(stackenv* se, const char* errmsg); |
-static void stackenv_uninit(stackenv* se); |
- |
-// Callback invoked by upb if any error occurs during parsing or serialization. |
-static bool env_error_func(void* ud, const upb_status* status) { |
- stackenv* se = ud; |
- // Free the env -- rb_raise will longjmp up the stack past the encode/decode |
- // function so it would not otherwise have been freed. |
- stackenv_uninit(se); |
- |
- // TODO(haberman): have a way to verify that this is actually a parse error, |
- // instead of just throwing "parse error" unconditionally. |
- rb_raise(cParseError, se->ruby_error_template, upb_status_errmsg(status)); |
- // Never reached: rb_raise() always longjmp()s up the stack, past all of our |
- // code, back to Ruby. |
- return false; |
-} |
- |
-static void stackenv_init(stackenv* se, const char* errmsg) { |
- se->ruby_error_template = errmsg; |
- upb_env_init2(&se->env, se->allocbuf, sizeof(se->allocbuf), NULL); |
- upb_env_seterrorfunc(&se->env, env_error_func, se); |
-} |
- |
-static void stackenv_uninit(stackenv* se) { |
- upb_env_uninit(&se->env); |
-} |
- |
-/* |
- * call-seq: |
- * MessageClass.decode(data) => message |
- * |
- * Decodes the given data (as a string containing bytes in protocol buffers wire |
- * format) under the interpretration given by this message class's definition |
- * and returns a message object with the corresponding field values. |
- */ |
-VALUE Message_decode(VALUE klass, VALUE data) { |
- VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); |
- Descriptor* desc = ruby_to_Descriptor(descriptor); |
- VALUE msgklass = Descriptor_msgclass(descriptor); |
- VALUE msg_rb; |
- MessageHeader* msg; |
- |
- if (TYPE(data) != T_STRING) { |
- rb_raise(rb_eArgError, "Expected string for binary protobuf data."); |
- } |
- |
- msg_rb = rb_class_new_instance(0, NULL, msgklass); |
- TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg); |
- |
- { |
- const upb_pbdecodermethod* method = msgdef_decodermethod(desc); |
- const upb_handlers* h = upb_pbdecodermethod_desthandlers(method); |
- stackenv se; |
- upb_sink sink; |
- upb_pbdecoder* decoder; |
- stackenv_init(&se, "Error occurred during parsing: %s"); |
- |
- upb_sink_reset(&sink, h, msg); |
- decoder = upb_pbdecoder_create(&se.env, method, &sink); |
- upb_bufsrc_putbuf(RSTRING_PTR(data), RSTRING_LEN(data), |
- upb_pbdecoder_input(decoder)); |
- |
- stackenv_uninit(&se); |
- } |
- |
- return msg_rb; |
-} |
- |
-/* |
- * call-seq: |
- * MessageClass.decode_json(data) => message |
- * |
- * Decodes the given data (as a string containing bytes in protocol buffers wire |
- * format) under the interpretration given by this message class's definition |
- * and returns a message object with the corresponding field values. |
- */ |
-VALUE Message_decode_json(VALUE klass, VALUE data) { |
- VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); |
- Descriptor* desc = ruby_to_Descriptor(descriptor); |
- VALUE msgklass = Descriptor_msgclass(descriptor); |
- VALUE msg_rb; |
- MessageHeader* msg; |
- |
- if (TYPE(data) != T_STRING) { |
- rb_raise(rb_eArgError, "Expected string for JSON data."); |
- } |
- // TODO(cfallin): Check and respect string encoding. If not UTF-8, we need to |
- // convert, because string handlers pass data directly to message string |
- // fields. |
- |
- msg_rb = rb_class_new_instance(0, NULL, msgklass); |
- TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg); |
- |
- { |
- const upb_json_parsermethod* method = msgdef_jsonparsermethod(desc); |
- stackenv se; |
- upb_sink sink; |
- upb_json_parser* parser; |
- stackenv_init(&se, "Error occurred during parsing: %s"); |
- |
- upb_sink_reset(&sink, get_fill_handlers(desc), msg); |
- parser = upb_json_parser_create(&se.env, method, &sink); |
- upb_bufsrc_putbuf(RSTRING_PTR(data), RSTRING_LEN(data), |
- upb_json_parser_input(parser)); |
- |
- stackenv_uninit(&se); |
- } |
- |
- return msg_rb; |
-} |
- |
// ----------------------------------------------------------------------------- |
// Serializing. |
// ----------------------------------------------------------------------------- |
-// |
-// The code below also comes from upb's prototype Ruby binding, developed by |
-// haberman@. |
-/* stringsink *****************************************************************/ |
+static void putmsg(zval* msg, const Descriptor* desc, upb_sink* sink, |
+ int depth TSRMLS_DC); |
-// This should probably be factored into a common upb component. |
+static void putstr(zval* str, const upb_fielddef* f, upb_sink* sink); |
-typedef struct { |
- upb_byteshandler handler; |
- upb_bytessink sink; |
- char *ptr; |
- size_t len, size; |
-} stringsink; |
+static void putrawstr(const char* str, int len, const upb_fielddef* f, |
+ upb_sink* sink); |
-static void *stringsink_start(void *_sink, const void *hd, size_t size_hint) { |
- stringsink *sink = _sink; |
- sink->len = 0; |
- return sink; |
-} |
- |
-static size_t stringsink_string(void *_sink, const void *hd, const char *ptr, |
- size_t len, const upb_bufhandle *handle) { |
- stringsink *sink = _sink; |
- size_t new_size = sink->size; |
- |
- UPB_UNUSED(hd); |
- UPB_UNUSED(handle); |
- |
- while (sink->len + len > new_size) { |
- new_size *= 2; |
- } |
- |
- if (new_size != sink->size) { |
- sink->ptr = realloc(sink->ptr, new_size); |
- sink->size = new_size; |
- } |
- |
- memcpy(sink->ptr + sink->len, ptr, len); |
- sink->len += len; |
- |
- return len; |
-} |
+static void putsubmsg(zval* submsg, const upb_fielddef* f, upb_sink* sink, |
+ int depth TSRMLS_DC); |
-void stringsink_init(stringsink *sink) { |
- upb_byteshandler_init(&sink->handler); |
- upb_byteshandler_setstartstr(&sink->handler, stringsink_start, NULL); |
- upb_byteshandler_setstring(&sink->handler, stringsink_string, NULL); |
- |
- upb_bytessink_reset(&sink->sink, &sink->handler, sink); |
- |
- sink->size = 32; |
- sink->ptr = malloc(sink->size); |
- sink->len = 0; |
-} |
- |
-void stringsink_uninit(stringsink *sink) { |
- free(sink->ptr); |
-} |
- |
-/* msgvisitor *****************************************************************/ |
+static void putarray(zval* array, const upb_fielddef* f, upb_sink* sink, |
+ int depth TSRMLS_DC); |
+static void putmap(zval* map, const upb_fielddef* f, upb_sink* sink, int depth |
+ TSRMLS_DC); |
-// TODO: If/when we support proto2 semantics in addition to the current proto3 |
-// semantics, which means that we have true field presence, we will want to |
-// modify msgvisitor so that it emits all present fields rather than all |
-// non-default-value fields. |
-// |
-// Likewise, when implementing JSON serialization, we may need to have a |
-// 'verbose' mode that outputs all fields and a 'concise' mode that outputs only |
-// those with non-default values. |
- |
-static void putmsg(VALUE msg, const Descriptor* desc, |
- upb_sink *sink, int depth); |
- |
-static upb_selector_t getsel(const upb_fielddef *f, upb_handlertype_t type) { |
+static upb_selector_t getsel(const upb_fielddef* f, upb_handlertype_t type) { |
upb_selector_t ret; |
bool ok = upb_handlers_getselector(f, type, &ret); |
- UPB_ASSERT_VAR(ok, ok); |
+ UPB_ASSERT(ok); |
return ret; |
} |
-static void putstr(VALUE str, const upb_fielddef *f, upb_sink *sink) { |
- upb_sink subsink; |
- |
- if (str == Qnil) return; |
- |
- assert(BUILTIN_TYPE(str) == RUBY_T_STRING); |
- |
- // Ensure that the string has the correct encoding. We also check at field-set |
- // time, but the user may have mutated the string object since then. |
- native_slot_validate_string_encoding(upb_fielddef_type(f), str); |
- |
- upb_sink_startstr(sink, getsel(f, UPB_HANDLER_STARTSTR), RSTRING_LEN(str), |
- &subsink); |
- upb_sink_putstring(&subsink, getsel(f, UPB_HANDLER_STRING), RSTRING_PTR(str), |
- RSTRING_LEN(str), NULL); |
- upb_sink_endstr(sink, getsel(f, UPB_HANDLER_ENDSTR)); |
-} |
+static void put_optional_value(const void* memory, int len, const upb_fielddef* f, |
+ int depth, upb_sink* sink TSRMLS_DC) { |
+ assert(upb_fielddef_label(f) == UPB_LABEL_OPTIONAL); |
-static void putsubmsg(VALUE submsg, const upb_fielddef *f, upb_sink *sink, |
- int depth) { |
- upb_sink subsink; |
- VALUE descriptor; |
- Descriptor* subdesc; |
- |
- if (submsg == Qnil) return; |
- |
- descriptor = rb_ivar_get(submsg, descriptor_instancevar_interned); |
- subdesc = ruby_to_Descriptor(descriptor); |
- |
- upb_sink_startsubmsg(sink, getsel(f, UPB_HANDLER_STARTSUBMSG), &subsink); |
- putmsg(submsg, subdesc, &subsink, depth + 1); |
- upb_sink_endsubmsg(sink, getsel(f, UPB_HANDLER_ENDSUBMSG)); |
-} |
- |
-static void putary(VALUE ary, const upb_fielddef *f, upb_sink *sink, |
- int depth) { |
- upb_sink subsink; |
- upb_fieldtype_t type = upb_fielddef_type(f); |
- upb_selector_t sel = 0; |
- int size; |
- |
- if (ary == Qnil) return; |
- |
- upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink); |
- |
- if (upb_fielddef_isprimitive(f)) { |
- sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); |
- } |
- |
- size = NUM2INT(RepeatedField_length(ary)); |
- for (int i = 0; i < size; i++) { |
- void* memory = RepeatedField_index_native(ary, i); |
- switch (type) { |
-#define T(upbtypeconst, upbtype, ctype) \ |
- case upbtypeconst: \ |
- upb_sink_put##upbtype(&subsink, sel, *((ctype *)memory)); \ |
- break; |
- |
- T(UPB_TYPE_FLOAT, float, float) |
- T(UPB_TYPE_DOUBLE, double, double) |
- T(UPB_TYPE_BOOL, bool, int8_t) |
- case UPB_TYPE_ENUM: |
- T(UPB_TYPE_INT32, int32, int32_t) |
- T(UPB_TYPE_UINT32, uint32, uint32_t) |
- T(UPB_TYPE_INT64, int64, int64_t) |
- T(UPB_TYPE_UINT64, uint64, uint64_t) |
- |
- case UPB_TYPE_STRING: |
- case UPB_TYPE_BYTES: |
- putstr(*((VALUE *)memory), f, &subsink); |
- break; |
- case UPB_TYPE_MESSAGE: |
- putsubmsg(*((VALUE *)memory), f, &subsink, depth); |
- break; |
+ switch (upb_fielddef_type(f)) { |
+#define T(upbtypeconst, upbtype, ctype, default_value) \ |
+ case upbtypeconst: { \ |
+ ctype value = DEREF(memory, 0, ctype); \ |
+ if (value != default_value) { \ |
+ upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); \ |
+ upb_sink_put##upbtype(sink, sel, value); \ |
+ } \ |
+ } break; |
+ |
+ T(UPB_TYPE_FLOAT, float, float, 0.0) |
+ T(UPB_TYPE_DOUBLE, double, double, 0.0) |
+ T(UPB_TYPE_BOOL, bool, uint8_t, 0) |
+ T(UPB_TYPE_ENUM, int32, int32_t, 0) |
+ T(UPB_TYPE_INT32, int32, int32_t, 0) |
+ T(UPB_TYPE_UINT32, uint32, uint32_t, 0) |
+ T(UPB_TYPE_INT64, int64, int64_t, 0) |
+ T(UPB_TYPE_UINT64, uint64, uint64_t, 0) |
#undef T |
- |
+ case UPB_TYPE_STRING: |
+ case UPB_TYPE_BYTES: |
+ putrawstr(memory, len, f, sink); |
+ break; |
+ case UPB_TYPE_MESSAGE: { |
+ zval* submsg = *(zval**)memory; |
+ putsubmsg(submsg, f, sink, depth TSRMLS_CC); |
+ break; |
} |
+ default: |
+ assert(false); |
} |
- upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ)); |
} |
-static void put_ruby_value(VALUE value, |
- const upb_fielddef *f, |
- VALUE type_class, |
- int depth, |
- upb_sink *sink) { |
- upb_selector_t sel = 0; |
- if (upb_fielddef_isprimitive(f)) { |
- sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); |
+// Only string/bytes fields are stored as zval. |
+static const char* raw_value(void* memory, const upb_fielddef* f) { |
+ switch (upb_fielddef_type(f)) { |
+ case UPB_TYPE_STRING: |
+ case UPB_TYPE_BYTES: |
+ return Z_STRVAL_PP((zval**)memory); |
+ break; |
+ default: |
+ return memory; |
} |
+} |
+static int raw_value_len(void* memory, int len, const upb_fielddef* f) { |
switch (upb_fielddef_type(f)) { |
- case UPB_TYPE_INT32: |
- upb_sink_putint32(sink, sel, NUM2INT(value)); |
- break; |
- case UPB_TYPE_INT64: |
- upb_sink_putint64(sink, sel, NUM2LL(value)); |
- break; |
- case UPB_TYPE_UINT32: |
- upb_sink_putuint32(sink, sel, NUM2UINT(value)); |
- break; |
- case UPB_TYPE_UINT64: |
- upb_sink_putuint64(sink, sel, NUM2ULL(value)); |
- break; |
- case UPB_TYPE_FLOAT: |
- upb_sink_putfloat(sink, sel, NUM2DBL(value)); |
- break; |
- case UPB_TYPE_DOUBLE: |
- upb_sink_putdouble(sink, sel, NUM2DBL(value)); |
- break; |
- case UPB_TYPE_ENUM: { |
- if (TYPE(value) == T_SYMBOL) { |
- value = rb_funcall(type_class, rb_intern("resolve"), 1, value); |
- } |
- upb_sink_putint32(sink, sel, NUM2INT(value)); |
- break; |
- } |
- case UPB_TYPE_BOOL: |
- upb_sink_putbool(sink, sel, value == Qtrue); |
- break; |
case UPB_TYPE_STRING: |
case UPB_TYPE_BYTES: |
- putstr(value, f, sink); |
+ return Z_STRLEN_PP((zval**)memory); |
break; |
- case UPB_TYPE_MESSAGE: |
- putsubmsg(value, f, sink, depth); |
+ default: |
+ return len; |
} |
} |
-static void putmap(VALUE map, const upb_fielddef *f, upb_sink *sink, |
- int depth) { |
+static void putmap(zval* map, const upb_fielddef* f, upb_sink* sink, |
+ int depth TSRMLS_DC) { |
Map* self; |
upb_sink subsink; |
const upb_fielddef* key_field; |
const upb_fielddef* value_field; |
- Map_iter it; |
+ MapIter it; |
+ int len; |
- if (map == Qnil) return; |
- self = ruby_to_Map(map); |
+ if (map == NULL) return; |
+ self = UNBOX(Map, map); |
upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink); |
@@ -1003,9 +972,7 @@ static void putmap(VALUE map, const upb_fielddef *f, upb_sink *sink, |
key_field = map_field_key(f); |
value_field = map_field_value(f); |
- for (Map_begin(map, &it); !Map_done(&it); Map_next(&it)) { |
- VALUE key = Map_iter_key(&it); |
- VALUE value = Map_iter_value(&it); |
+ for (map_begin(map, &it TSRMLS_CC); !map_done(&it); map_next(&it)) { |
upb_status status; |
upb_sink entry_sink; |
@@ -1013,9 +980,15 @@ static void putmap(VALUE map, const upb_fielddef *f, upb_sink *sink, |
&entry_sink); |
upb_sink_startmsg(&entry_sink); |
- put_ruby_value(key, key_field, Qnil, depth + 1, &entry_sink); |
- put_ruby_value(value, value_field, self->value_type_class, depth + 1, |
- &entry_sink); |
+ // Serialize key. |
+ const char *key = map_iter_key(&it, &len); |
+ put_optional_value(key, len, key_field, depth + 1, &entry_sink TSRMLS_CC); |
+ |
+ // Serialize value. |
+ upb_value value = map_iter_value(&it, &len); |
+ put_optional_value(raw_value(upb_value_memory(&value), value_field), |
+ raw_value_len(upb_value_memory(&value), len, value_field), |
+ value_field, depth + 1, &entry_sink TSRMLS_CC); |
upb_sink_endmsg(&entry_sink, &status); |
upb_sink_endsubmsg(&subsink, getsel(f, UPB_HANDLER_ENDSUBMSG)); |
@@ -1024,9 +997,8 @@ static void putmap(VALUE map, const upb_fielddef *f, upb_sink *sink, |
upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ)); |
} |
-static void putmsg(VALUE msg_rb, const Descriptor* desc, |
- upb_sink *sink, int depth) { |
- MessageHeader* msg; |
+static void putmsg(zval* msg_php, const Descriptor* desc, upb_sink* sink, |
+ int depth TSRMLS_DC) { |
upb_msg_field_iter i; |
upb_status status; |
@@ -1035,20 +1007,17 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc, |
// Protect against cycles (possible because users may freely reassign message |
// and repeated fields) by imposing a maximum recursion depth. |
if (depth > ENCODE_MAX_NESTING) { |
- rb_raise(rb_eRuntimeError, |
+ zend_error(E_ERROR, |
"Maximum recursion depth exceeded during encoding."); |
} |
- TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg); |
+ MessageHeader* msg = zend_object_store_get_object(msg_php TSRMLS_CC); |
- for (upb_msg_field_begin(&i, desc->msgdef); |
- !upb_msg_field_done(&i); |
+ for (upb_msg_field_begin(&i, desc->msgdef); !upb_msg_field_done(&i); |
upb_msg_field_next(&i)) { |
- upb_fielddef *f = upb_msg_iter_field(&i); |
- bool is_matching_oneof = false; |
- uint32_t offset = |
- desc->layout->fields[upb_fielddef_index(f)].offset + |
- sizeof(MessageHeader); |
+ upb_fielddef* f = upb_msg_iter_field(&i); |
+ uint32_t offset = desc->layout->fields[upb_fielddef_index(f)].offset + |
+ sizeof(MessageHeader); |
if (upb_fielddef_containingoneof(f)) { |
uint32_t oneof_case_offset = |
@@ -1056,67 +1025,170 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc, |
sizeof(MessageHeader); |
// For a oneof, check that this field is actually present -- skip all the |
// below if not. |
- if (DEREF(msg, oneof_case_offset, uint32_t) != |
- upb_fielddef_number(f)) { |
+ if (DEREF(msg, oneof_case_offset, uint32_t) != upb_fielddef_number(f)) { |
continue; |
} |
// Otherwise, fall through to the appropriate singular-field handler |
// below. |
- is_matching_oneof = true; |
} |
if (is_map_field(f)) { |
- VALUE map = DEREF(msg, offset, VALUE); |
- if (map != Qnil) { |
- putmap(map, f, sink, depth); |
+ zval* map = *DEREF(msg, offset, zval**); |
+ if (map != NULL) { |
+ putmap(map, f, sink, depth TSRMLS_CC); |
} |
} else if (upb_fielddef_isseq(f)) { |
- VALUE ary = DEREF(msg, offset, VALUE); |
- if (ary != Qnil) { |
- putary(ary, f, sink, depth); |
+ zval* array = *DEREF(msg, offset, zval**); |
+ if (array != NULL) { |
+ putarray(array, f, sink, depth TSRMLS_CC); |
} |
} else if (upb_fielddef_isstring(f)) { |
- VALUE str = DEREF(msg, offset, VALUE); |
- if (is_matching_oneof || RSTRING_LEN(str) > 0) { |
+ zval* str = *DEREF(msg, offset, zval**); |
+ if (Z_STRLEN_P(str) > 0) { |
putstr(str, f, sink); |
} |
} else if (upb_fielddef_issubmsg(f)) { |
- putsubmsg(DEREF(msg, offset, VALUE), f, sink, depth); |
+ putsubmsg(*DEREF(msg, offset, zval**), f, sink, depth TSRMLS_CC); |
} else { |
upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); |
-#define T(upbtypeconst, upbtype, ctype, default_value) \ |
- case upbtypeconst: { \ |
- ctype value = DEREF(msg, offset, ctype); \ |
- if (is_matching_oneof || value != default_value) { \ |
- upb_sink_put##upbtype(sink, sel, value); \ |
- } \ |
- } \ |
- break; |
+#define T(upbtypeconst, upbtype, ctype, default_value) \ |
+ case upbtypeconst: { \ |
+ ctype value = DEREF(msg, offset, ctype); \ |
+ if (value != default_value) { \ |
+ upb_sink_put##upbtype(sink, sel, value); \ |
+ } \ |
+ } break; |
switch (upb_fielddef_type(f)) { |
- T(UPB_TYPE_FLOAT, float, float, 0.0) |
+ T(UPB_TYPE_FLOAT, float, float, 0.0) |
T(UPB_TYPE_DOUBLE, double, double, 0.0) |
- T(UPB_TYPE_BOOL, bool, uint8_t, 0) |
+ T(UPB_TYPE_BOOL, bool, uint8_t, 0) |
case UPB_TYPE_ENUM: |
- T(UPB_TYPE_INT32, int32, int32_t, 0) |
- T(UPB_TYPE_UINT32, uint32, uint32_t, 0) |
- T(UPB_TYPE_INT64, int64, int64_t, 0) |
- T(UPB_TYPE_UINT64, uint64, uint64_t, 0) |
+ T(UPB_TYPE_INT32, int32, int32_t, 0) |
+ T(UPB_TYPE_UINT32, uint32, uint32_t, 0) |
+ T(UPB_TYPE_INT64, int64, int64_t, 0) |
+ T(UPB_TYPE_UINT64, uint64, uint64_t, 0) |
case UPB_TYPE_STRING: |
case UPB_TYPE_BYTES: |
- case UPB_TYPE_MESSAGE: rb_raise(rb_eRuntimeError, "Internal error."); |
+ case UPB_TYPE_MESSAGE: |
+ zend_error(E_ERROR, "Internal error."); |
} |
#undef T |
- |
} |
} |
upb_sink_endmsg(sink, &status); |
} |
+static void putstr(zval* str, const upb_fielddef *f, upb_sink *sink) { |
+ upb_sink subsink; |
+ |
+ if (ZVAL_IS_NULL(str)) return; |
+ |
+ assert(Z_TYPE_P(str) == IS_STRING); |
+ |
+ // Ensure that the string has the correct encoding. We also check at field-set |
+ // time, but the user may have mutated the string object since then. |
+ if (upb_fielddef_type(f) == UPB_TYPE_STRING && |
+ !is_structurally_valid_utf8(Z_STRVAL_P(str), Z_STRLEN_P(str))) { |
+ zend_error(E_USER_ERROR, "Given string is not UTF8 encoded."); |
+ return; |
+ } |
+ |
+ upb_sink_startstr(sink, getsel(f, UPB_HANDLER_STARTSTR), Z_STRLEN_P(str), |
+ &subsink); |
+ upb_sink_putstring(&subsink, getsel(f, UPB_HANDLER_STRING), Z_STRVAL_P(str), |
+ Z_STRLEN_P(str), NULL); |
+ upb_sink_endstr(sink, getsel(f, UPB_HANDLER_ENDSTR)); |
+} |
+ |
+static void putrawstr(const char* str, int len, const upb_fielddef* f, |
+ upb_sink* sink) { |
+ upb_sink subsink; |
+ |
+ if (len == 0) return; |
+ |
+ // Ensure that the string has the correct encoding. We also check at field-set |
+ // time, but the user may have mutated the string object since then. |
+ if (upb_fielddef_type(f) == UPB_TYPE_STRING && |
+ !is_structurally_valid_utf8(str, len)) { |
+ zend_error(E_USER_ERROR, "Given string is not UTF8 encoded."); |
+ return; |
+ } |
+ |
+ upb_sink_startstr(sink, getsel(f, UPB_HANDLER_STARTSTR), len, &subsink); |
+ upb_sink_putstring(&subsink, getsel(f, UPB_HANDLER_STRING), str, len, NULL); |
+ upb_sink_endstr(sink, getsel(f, UPB_HANDLER_ENDSTR)); |
+} |
+ |
+static void putsubmsg(zval* submsg, const upb_fielddef* f, upb_sink* sink, |
+ int depth TSRMLS_DC) { |
+ upb_sink subsink; |
+ |
+ if (Z_TYPE_P(submsg) == IS_NULL) return; |
+ |
+ zval* php_descriptor = get_def_obj(upb_fielddef_msgsubdef(f)); |
+ Descriptor* subdesc = |
+ (Descriptor*)zend_object_store_get_object(php_descriptor TSRMLS_CC); |
+ |
+ upb_sink_startsubmsg(sink, getsel(f, UPB_HANDLER_STARTSUBMSG), &subsink); |
+ putmsg(submsg, subdesc, &subsink, depth + 1 TSRMLS_CC); |
+ upb_sink_endsubmsg(sink, getsel(f, UPB_HANDLER_ENDSUBMSG)); |
+} |
+ |
+static void putarray(zval* array, const upb_fielddef* f, upb_sink* sink, |
+ int depth TSRMLS_DC) { |
+ upb_sink subsink; |
+ upb_fieldtype_t type = upb_fielddef_type(f); |
+ upb_selector_t sel = 0; |
+ int size, i; |
+ |
+ assert(array != NULL); |
+ RepeatedField* intern = |
+ (RepeatedField*)zend_object_store_get_object(array TSRMLS_CC); |
+ size = zend_hash_num_elements(HASH_OF(intern->array)); |
+ if (size == 0) return; |
+ |
+ upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink); |
+ |
+ if (upb_fielddef_isprimitive(f)) { |
+ sel = getsel(f, upb_handlers_getprimitivehandlertype(f)); |
+ } |
+ |
+ for (i = 0; i < size; i++) { |
+ void* memory = repeated_field_index_native(intern, i TSRMLS_CC); |
+ switch (type) { |
+#define T(upbtypeconst, upbtype, ctype) \ |
+ case upbtypeconst: \ |
+ upb_sink_put##upbtype(&subsink, sel, *((ctype*)memory)); \ |
+ break; |
+ |
+ T(UPB_TYPE_FLOAT, float, float) |
+ T(UPB_TYPE_DOUBLE, double, double) |
+ T(UPB_TYPE_BOOL, bool, int8_t) |
+ case UPB_TYPE_ENUM: |
+ T(UPB_TYPE_INT32, int32, int32_t) |
+ T(UPB_TYPE_UINT32, uint32, uint32_t) |
+ T(UPB_TYPE_INT64, int64, int64_t) |
+ T(UPB_TYPE_UINT64, uint64, uint64_t) |
+ |
+ case UPB_TYPE_STRING: |
+ case UPB_TYPE_BYTES: |
+ putstr(*((zval**)memory), f, &subsink); |
+ break; |
+ case UPB_TYPE_MESSAGE: |
+ putsubmsg(*((zval**)memory), f, &subsink, depth TSRMLS_CC); |
+ break; |
+ |
+#undef T |
+ } |
+ } |
+ upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ)); |
+} |
+ |
static const upb_handlers* msgdef_pb_serialize_handlers(Descriptor* desc) { |
if (desc->pb_serialize_handlers == NULL) { |
desc->pb_serialize_handlers = |
@@ -1125,109 +1197,61 @@ static const upb_handlers* msgdef_pb_serialize_handlers(Descriptor* desc) { |
return desc->pb_serialize_handlers; |
} |
-static const upb_handlers* msgdef_json_serialize_handlers( |
- Descriptor* desc, bool preserve_proto_fieldnames) { |
- if (preserve_proto_fieldnames) { |
- if (desc->json_serialize_handlers == NULL) { |
- desc->json_serialize_handlers = |
- upb_json_printer_newhandlers( |
- desc->msgdef, true, &desc->json_serialize_handlers); |
- } |
- return desc->json_serialize_handlers; |
- } else { |
- if (desc->json_serialize_handlers_preserve == NULL) { |
- desc->json_serialize_handlers_preserve = |
- upb_json_printer_newhandlers( |
- desc->msgdef, false, &desc->json_serialize_handlers_preserve); |
- } |
- return desc->json_serialize_handlers_preserve; |
- } |
-} |
+// ----------------------------------------------------------------------------- |
+// PHP encode/decode methods |
+// ----------------------------------------------------------------------------- |
-/* |
- * call-seq: |
- * MessageClass.encode(msg) => bytes |
- * |
- * Encodes the given message object to its serialized form in protocol buffers |
- * wire format. |
- */ |
-VALUE Message_encode(VALUE klass, VALUE msg_rb) { |
- VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); |
- Descriptor* desc = ruby_to_Descriptor(descriptor); |
+PHP_METHOD(Message, encode) { |
+ zval* php_descriptor = get_ce_obj(Z_OBJCE_P(getThis())); |
+ Descriptor* desc = |
+ (Descriptor*)zend_object_store_get_object(php_descriptor TSRMLS_CC); |
stringsink sink; |
stringsink_init(&sink); |
{ |
- const upb_handlers* serialize_handlers = |
- msgdef_pb_serialize_handlers(desc); |
+ const upb_handlers* serialize_handlers = msgdef_pb_serialize_handlers(desc); |
stackenv se; |
upb_pb_encoder* encoder; |
- VALUE ret; |
stackenv_init(&se, "Error occurred during encoding: %s"); |
encoder = upb_pb_encoder_create(&se.env, serialize_handlers, &sink.sink); |
- putmsg(msg_rb, desc, upb_pb_encoder_input(encoder), 0); |
+ putmsg(getThis(), desc, upb_pb_encoder_input(encoder), 0 TSRMLS_CC); |
- ret = rb_str_new(sink.ptr, sink.len); |
+ RETVAL_STRINGL(sink.ptr, sink.len, 1); |
stackenv_uninit(&se); |
stringsink_uninit(&sink); |
- |
- return ret; |
} |
} |
-/* |
- * call-seq: |
- * MessageClass.encode_json(msg) => json_string |
- * |
- * Encodes the given message object into its serialized JSON representation. |
- */ |
-VALUE Message_encode_json(int argc, VALUE* argv, VALUE klass) { |
- VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned); |
- Descriptor* desc = ruby_to_Descriptor(descriptor); |
- VALUE msg_rb; |
- VALUE preserve_proto_fieldnames = Qfalse; |
- stringsink sink; |
+PHP_METHOD(Message, decode) { |
+ zval* php_descriptor = get_ce_obj(Z_OBJCE_P(getThis())); |
+ Descriptor* desc = |
+ (Descriptor*)zend_object_store_get_object(php_descriptor TSRMLS_CC); |
+ MessageHeader* msg = zend_object_store_get_object(getThis() TSRMLS_CC); |
- if (argc < 1 || argc > 2) { |
- rb_raise(rb_eArgError, "Expected 1 or 2 arguments."); |
- } |
- |
- msg_rb = argv[0]; |
- |
- if (argc == 2) { |
- VALUE hash_args = argv[1]; |
- if (TYPE(hash_args) != T_HASH) { |
- rb_raise(rb_eArgError, "Expected hash arguments."); |
- } |
- preserve_proto_fieldnames = rb_hash_lookup2( |
- hash_args, ID2SYM(rb_intern("preserve_proto_fieldnames")), Qfalse); |
+ char *data = NULL; |
+ int data_len; |
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &data, &data_len) == |
+ FAILURE) { |
+ return; |
} |
- stringsink_init(&sink); |
- |
{ |
- const upb_handlers* serialize_handlers = |
- msgdef_json_serialize_handlers(desc, RTEST(preserve_proto_fieldnames)); |
- upb_json_printer* printer; |
+ const upb_pbdecodermethod* method = msgdef_decodermethod(desc); |
+ const upb_handlers* h = upb_pbdecodermethod_desthandlers(method); |
stackenv se; |
- VALUE ret; |
- |
- stackenv_init(&se, "Error occurred during encoding: %s"); |
- printer = upb_json_printer_create(&se.env, serialize_handlers, &sink.sink); |
- |
- putmsg(msg_rb, desc, upb_json_printer_input(printer), 0); |
+ upb_sink sink; |
+ upb_pbdecoder* decoder; |
+ stackenv_init(&se, "Error occurred during parsing: %s"); |
- ret = rb_enc_str_new(sink.ptr, sink.len, rb_utf8_encoding()); |
+ upb_sink_reset(&sink, h, msg); |
+ decoder = upb_pbdecoder_create(&se.env, method, &sink); |
+ upb_bufsrc_putbuf(data, data_len, upb_pbdecoder_input(decoder)); |
stackenv_uninit(&se); |
- stringsink_uninit(&sink); |
- |
- return ret; |
} |
} |
- |