diff options
Diffstat (limited to 'php/ext/google')
-rw-r--r-- | php/ext/google/protobuf/array.c | 127 | ||||
-rw-r--r-- | php/ext/google/protobuf/def.c | 55 | ||||
-rw-r--r-- | php/ext/google/protobuf/message.c | 4 | ||||
-rw-r--r-- | php/ext/google/protobuf/protobuf.c | 1 | ||||
-rw-r--r-- | php/ext/google/protobuf/protobuf.h | 19 | ||||
-rw-r--r-- | php/ext/google/protobuf/storage.c | 24 | ||||
-rw-r--r-- | php/ext/google/protobuf/type_check.c | 139 |
7 files changed, 333 insertions, 36 deletions
diff --git a/php/ext/google/protobuf/array.c b/php/ext/google/protobuf/array.c index 215dcd46..e4a88c39 100644 --- a/php/ext/google/protobuf/array.c +++ b/php/ext/google/protobuf/array.c @@ -54,6 +54,16 @@ static zend_function_entry repeated_field_methods[] = { PHP_ME(RepeatedField, offsetSet, arginfo_offsetSet, ZEND_ACC_PUBLIC) PHP_ME(RepeatedField, offsetUnset, arginfo_offsetGet, ZEND_ACC_PUBLIC) PHP_ME(RepeatedField, count, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RepeatedField, getIterator, arginfo_void, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + +static zend_function_entry repeated_field_iter_methods[] = { + PHP_ME(RepeatedFieldIter, rewind, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RepeatedFieldIter, current, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RepeatedFieldIter, key, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RepeatedFieldIter, next, arginfo_void, ZEND_ACC_PUBLIC) + PHP_ME(RepeatedFieldIter, valid, arginfo_void, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -70,11 +80,15 @@ static int repeated_field_has_dimension(zval *object, zval *offset TSRMLS_DC); static HashTable *repeated_field_get_gc(zval *object, zval ***table, int *n TSRMLS_DC); +static zend_object_value repeated_field_iter_create(zend_class_entry *ce TSRMLS_DC); +static void repeated_field_iter_free(void *object TSRMLS_DC); + // ----------------------------------------------------------------------------- // RepeatedField creation/desctruction // ----------------------------------------------------------------------------- zend_class_entry* repeated_field_type; +zend_class_entry* repeated_field_iter_type; zend_object_handlers* repeated_field_handlers; void repeated_field_init(TSRMLS_D) { @@ -86,8 +100,8 @@ void repeated_field_init(TSRMLS_D) { repeated_field_type = zend_register_internal_class(&class_type TSRMLS_CC); repeated_field_type->create_object = repeated_field_create; - zend_class_implements(repeated_field_type TSRMLS_CC, 2, spl_ce_ArrayAccess, - spl_ce_Countable); + zend_class_implements(repeated_field_type TSRMLS_CC, 3, spl_ce_ArrayAccess, + zend_ce_aggregate, spl_ce_Countable); repeated_field_handlers = PEMALLOC(zend_object_handlers); memcpy(repeated_field_handlers, zend_get_std_object_handlers(), @@ -386,3 +400,112 @@ PHP_METHOD(RepeatedField, count) { RETURN_LONG(zend_hash_num_elements(HASH_OF(intern->array))); } + +/** + * Return the beginning iterator. + * This will also be called for: foreach($arr) + * @return object Beginning iterator. + */ +PHP_METHOD(RepeatedField, getIterator) { + zval *iter_php = NULL; + MAKE_STD_ZVAL(iter_php); + Z_TYPE_P(iter_php) = IS_OBJECT; + Z_OBJVAL_P(iter_php) = repeated_field_iter_type->create_object( + repeated_field_iter_type TSRMLS_CC); + + RepeatedField *intern = zend_object_store_get_object(getThis() TSRMLS_CC); + RepeatedFieldIter *iter = zend_object_store_get_object(iter_php TSRMLS_CC); + iter->repeated_field = intern; + iter->position = 0; + + RETURN_ZVAL(iter_php, 1, 1); +} + +// ----------------------------------------------------------------------------- +// RepeatedFieldIter creation/desctruction +// ----------------------------------------------------------------------------- + +void repeated_field_iter_init(TSRMLS_D) { + zend_class_entry class_type; + const char* class_name = "Google\\Protobuf\\Internal\\RepeatedFieldIter"; + INIT_CLASS_ENTRY_EX(class_type, class_name, strlen(class_name), + repeated_field_iter_methods); + + repeated_field_iter_type = + zend_register_internal_class(&class_type TSRMLS_CC); + repeated_field_iter_type->create_object = repeated_field_iter_create; + + zend_class_implements(repeated_field_iter_type TSRMLS_CC, 1, + zend_ce_iterator); +} + +static zend_object_value repeated_field_iter_create( + zend_class_entry *ce TSRMLS_DC) { + zend_object_value retval = {0}; + RepeatedFieldIter *intern; + + intern = emalloc(sizeof(RepeatedFieldIter)); + memset(intern, 0, sizeof(RepeatedFieldIter)); + + zend_object_std_init(&intern->std, ce TSRMLS_CC); + object_properties_init(&intern->std, ce); + + intern->repeated_field = NULL; + intern->position = 0; + + retval.handle = zend_objects_store_put( + intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, + (zend_objects_free_object_storage_t)repeated_field_iter_free, + NULL TSRMLS_CC); + retval.handlers = zend_get_std_object_handlers(); + + return retval; +} + +static void repeated_field_iter_free(void *object TSRMLS_DC) { + RepeatedFieldIter *intern = object; + zend_object_std_dtor(&intern->std TSRMLS_CC); + efree(object); +} + +// ----------------------------------------------------------------------------- +// PHP RepeatedFieldIter Methods +// ----------------------------------------------------------------------------- + +PHP_METHOD(RepeatedFieldIter, rewind) { + RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); + intern->position = 0; +} + +PHP_METHOD(RepeatedFieldIter, current) { + RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); + RepeatedField *repeated_field = intern->repeated_field; + + long index; + void *memory; + + HashTable *table = HASH_OF(repeated_field->array); + + if (zend_hash_index_find(table, intern->position, (void **)&memory) == + FAILURE) { + zend_error(E_USER_ERROR, "Element at %ld doesn't exist.\n", index); + return; + } + native_slot_get(repeated_field->type, memory, return_value_ptr TSRMLS_CC); +} + +PHP_METHOD(RepeatedFieldIter, key) { + RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); + RETURN_LONG(intern->position); +} + +PHP_METHOD(RepeatedFieldIter, next) { + RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); + ++intern->position; +} + +PHP_METHOD(RepeatedFieldIter, valid) { + RepeatedFieldIter *intern = zend_object_store_get_object(getThis() TSRMLS_CC); + RETURN_BOOL(zend_hash_num_elements(HASH_OF(intern->repeated_field->array)) > + intern->position); +} diff --git a/php/ext/google/protobuf/def.c b/php/ext/google/protobuf/def.c index 156eca07..6ea2cc93 100644 --- a/php/ext/google/protobuf/def.c +++ b/php/ext/google/protobuf/def.c @@ -250,28 +250,41 @@ PHP_METHOD(DescriptorPool, getGeneratedPool) { RETURN_ZVAL(generated_pool_php, 1, 0); } -static void convert_to_class_name_inplace(char *proto_name, - size_t pkg_name_len) { +static void convert_to_class_name_inplace(char *class_name, + const char* fullname, + const char* package_name) { size_t i; bool first_char = false; + size_t pkg_name_len = package_name == NULL ? 0 : strlen(package_name); - for (i = 0; i <= pkg_name_len + 1; i++) { - // PHP package uses camel case. - if (!first_char && proto_name[i] != '.') { - first_char = true; - proto_name[i] += 'A' - 'a'; - } - // php packages are divided by '\'. - if (proto_name[i] == '.') { - first_char = false; - proto_name[i] = '\\'; + // In php, class name cannot be Empty. + if (strcmp("google.protobuf.Empty", fullname) == 0) { + fullname = "google.protobuf.GPBEmpty"; + } + + if (pkg_name_len == 0) { + strcpy(class_name, fullname); + } else { + class_name[0] = '.'; + strcpy(&class_name[1], fullname); + for (i = 0; i <= pkg_name_len + 1; i++) { + // PHP package uses camel case. + if (!first_char && class_name[i] != '.') { + first_char = true; + class_name[i] += 'A' - 'a'; + } + // php packages are divided by '\'. + if (class_name[i] == '.') { + first_char = false; + class_name[i] = '\\'; + } } } // Submessage is concatenated with its containing messages by '_'. - for (i = pkg_name_len; i < strlen(proto_name); i++) { - if (proto_name[i] == '.') { - proto_name[i] = '_'; + for (i = pkg_name_len; i < strlen(class_name); i++) { + if (class_name[i] == '.') { + class_name[i] = '_'; } } } @@ -322,13 +335,13 @@ PHP_METHOD(DescriptorPool, internalAddGeneratedFile) { upb_msgdef_mapentry(upb_downcast_msgdef(def))) { \ break; \ } \ - /* Prepend '.' to package name to make it absolute. */ \ + /* Prepend '.' to package name to make it absolute. In the 5 additional \ + * bytes allocated, one for '.', one for trailing 0, and 3 for 'GPB' if \ + * given message is google.protobuf.Empty.*/ \ const char *fullname = upb_##def_type_lower##_fullname(def_type_lower); \ - char *klass_name = ecalloc(sizeof(char), 2 + strlen(fullname)); \ - klass_name[0] = '.'; \ - strcpy(&klass_name[1], fullname); \ - size_t pkg_name_len = strlen(upb_filedef_package(files[0])); \ - convert_to_class_name_inplace(klass_name, pkg_name_len); \ + char *klass_name = ecalloc(sizeof(char), 5 + strlen(fullname)); \ + convert_to_class_name_inplace(klass_name, fullname, \ + upb_filedef_package(files[0])); \ zend_class_entry **pce; \ if (zend_lookup_class(klass_name, strlen(klass_name), &pce TSRMLS_CC) == \ FAILURE) { \ diff --git a/php/ext/google/protobuf/message.c b/php/ext/google/protobuf/message.c index cb46031e..d8fbbe11 100644 --- a/php/ext/google/protobuf/message.c +++ b/php/ext/google/protobuf/message.c @@ -177,8 +177,8 @@ static zend_object_value message_create(zend_class_entry* ce TSRMLS_DC) { zend_object_std_init(&msg->std, ce TSRMLS_CC); object_properties_init(&msg->std, ce); - layout_init(desc->layout, message_data(msg), msg->std.properties_table - TSRMLS_CC); + layout_init(desc->layout, message_data(msg), + msg->std.properties_table TSRMLS_CC); return_value.handle = zend_objects_store_put( msg, (zend_objects_store_dtor_t)zend_objects_destroy_object, message_free, diff --git a/php/ext/google/protobuf/protobuf.c b/php/ext/google/protobuf/protobuf.c index 019bca29..ea85b999 100644 --- a/php/ext/google/protobuf/protobuf.c +++ b/php/ext/google/protobuf/protobuf.c @@ -156,6 +156,7 @@ static PHP_RSHUTDOWN_FUNCTION(protobuf) { static PHP_MINIT_FUNCTION(protobuf) { map_field_init(TSRMLS_C); repeated_field_init(TSRMLS_C); + repeated_field_iter_init(TSRMLS_C); gpb_type_init(TSRMLS_C); message_init(TSRMLS_C); descriptor_pool_init(TSRMLS_C); diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h index 8a1d9261..fb5879dc 100644 --- a/php/ext/google/protobuf/protobuf.h +++ b/php/ext/google/protobuf/protobuf.h @@ -39,6 +39,9 @@ #define PHP_PROTOBUF_EXTNAME "protobuf" #define PHP_PROTOBUF_VERSION "3.1.0a1" +#define MAX_LENGTH_OF_INT64 20 +#define SIZEOF_INT64 8 + // ----------------------------------------------------------------------------- // Forward Declaration // ---------------------------------------------------------------------------- @@ -51,6 +54,7 @@ struct MessageField; struct MessageHeader; struct MessageLayout; struct RepeatedField; +struct RepeatedFieldIter; struct MapField; typedef struct DescriptorPool DescriptorPool; @@ -61,6 +65,7 @@ typedef struct MessageField MessageField; typedef struct MessageHeader MessageHeader; typedef struct MessageLayout MessageLayout; typedef struct RepeatedField RepeatedField; +typedef struct RepeatedFieldIter RepeatedFieldIter; typedef struct MapField MapField; // ----------------------------------------------------------------------------- @@ -77,6 +82,7 @@ void descriptor_pool_init(TSRMLS_D); void gpb_type_init(TSRMLS_D); void map_field_init(TSRMLS_D); void repeated_field_init(TSRMLS_D); +void repeated_field_iter_init(TSRMLS_D); void util_init(TSRMLS_D); void message_init(TSRMLS_D); @@ -366,6 +372,12 @@ struct RepeatedField { // (for message field only). }; +struct RepeatedFieldIter { + zend_object std; + RepeatedField* repeated_field; + long position; +}; + void repeated_field_create_with_type(zend_class_entry* ce, const upb_fielddef* field, zval** repeated_field TSRMLS_DC); @@ -383,6 +395,13 @@ PHP_METHOD(RepeatedField, offsetGet); PHP_METHOD(RepeatedField, offsetSet); PHP_METHOD(RepeatedField, offsetUnset); PHP_METHOD(RepeatedField, count); +PHP_METHOD(RepeatedField, getIterator); + +PHP_METHOD(RepeatedFieldIter, rewind); +PHP_METHOD(RepeatedFieldIter, current); +PHP_METHOD(RepeatedFieldIter, key); +PHP_METHOD(RepeatedFieldIter, next); +PHP_METHOD(RepeatedFieldIter, valid); // ----------------------------------------------------------------------------- // Oneof Field. diff --git a/php/ext/google/protobuf/storage.c b/php/ext/google/protobuf/storage.c index e94aa319..1d25a91b 100644 --- a/php/ext/google/protobuf/storage.c +++ b/php/ext/google/protobuf/storage.c @@ -174,11 +174,31 @@ CASE(FLOAT, DOUBLE, float) CASE(DOUBLE, DOUBLE, double) CASE(BOOL, BOOL, int8_t) CASE(INT32, LONG, int32_t) -CASE(INT64, LONG, int64_t) -CASE(UINT64, LONG, uint64_t) CASE(ENUM, LONG, uint32_t) #undef CASE + +#if SIZEOF_LONG == 4 +#define CASE(upb_type, c_type) \ + case UPB_TYPE_##upb_type: { \ + SEPARATE_ZVAL_IF_NOT_REF(cache); \ + char buffer[MAX_LENGTH_OF_INT64]; \ + sprintf(buffer, "%lld", DEREF(memory, c_type)); \ + ZVAL_STRING(*cache, buffer, 1); \ + return; \ + } +#else +#define CASE(upb_type, c_type) \ + case UPB_TYPE_##upb_type: { \ + SEPARATE_ZVAL_IF_NOT_REF(cache); \ + ZVAL_LONG(*cache, DEREF(memory, c_type)); \ + return; \ + } +#endif +CASE(UINT64, uint64_t) +CASE(INT64, int64_t) +#undef CASE + case UPB_TYPE_UINT32: { // Prepend bit-1 for negative numbers, so that uint32 value will be // consistent on both 32-bit and 64-bit architectures. diff --git a/php/ext/google/protobuf/type_check.c b/php/ext/google/protobuf/type_check.c index c215d72e..d12d0025 100644 --- a/php/ext/google/protobuf/type_check.c +++ b/php/ext/google/protobuf/type_check.c @@ -34,6 +34,7 @@ #include "utf8.h" static zend_class_entry* util_type; +static const char int64_min_digits[] = "9223372036854775808"; ZEND_BEGIN_ARG_INFO_EX(arg_check_optional, 0, 0, 1) ZEND_ARG_INFO(1, val) @@ -78,8 +79,128 @@ void util_init(TSRMLS_D) { // Type checking/conversion. // ----------------------------------------------------------------------------- +// This is modified from is_numeric_string in zend_operators.h. The behavior of +// this function is the same as is_numeric_string, except that this takes +// int64_t as input instead of long. +static zend_uchar convert_numeric_string( + const char *str, int length, int64_t *lval, double *dval) { + const char *ptr; + int base = 10, digits = 0, dp_or_e = 0; + double local_dval = 0.0; + zend_uchar type; + + if (length == 0) { + return IS_NULL; + } + + while (*str == ' ' || *str == '\t' || *str == '\n' || + *str == '\r' || *str == '\v' || *str == '\f') { + str++; + length--; + } + ptr = str; + + if (*ptr == '-' || *ptr == '+') { + ptr++; + } + + if (ZEND_IS_DIGIT(*ptr)) { + // Handle hex numbers + // str is used instead of ptr to disallow signs and keep old behavior. + if (length > 2 && *str == '0' && (str[1] == 'x' || str[1] == 'X')) { + base = 16; + ptr += 2; + } + + // Skip any leading 0s. + while (*ptr == '0') { + ptr++; + } + + // Count the number of digits. If a decimal point/exponent is found, + // it's a double. Otherwise, if there's a dval or no need to check for + // a full match, stop when there are too many digits for a int64 */ + for (type = IS_LONG; + !(digits >= MAX_LENGTH_OF_INT64 && dval); + digits++, ptr++) { +check_digits: + if (ZEND_IS_DIGIT(*ptr) || (base == 16 && ZEND_IS_XDIGIT(*ptr))) { + continue; + } else if (base == 10) { + if (*ptr == '.' && dp_or_e < 1) { + goto process_double; + } else if ((*ptr == 'e' || *ptr == 'E') && dp_or_e < 2) { + const char *e = ptr + 1; + + if (*e == '-' || *e == '+') { + ptr = e++; + } + if (ZEND_IS_DIGIT(*e)) { + goto process_double; + } + } + } + break; + } + + if (base == 10) { + if (digits >= MAX_LENGTH_OF_INT64) { + dp_or_e = -1; + goto process_double; + } + } else if (!(digits < SIZEOF_INT64 * 2 || + (digits == SIZEOF_INT64 * 2 && ptr[-digits] <= '7'))) { + if (dval) { + local_dval = zend_hex_strtod(str, &ptr); + } + type = IS_DOUBLE; + } + } else if (*ptr == '.' && ZEND_IS_DIGIT(ptr[1])) { +process_double: + type = IS_DOUBLE; + + // If there's a dval, do the conversion; else continue checking + // the digits if we need to check for a full match. + if (dval) { + local_dval = zend_strtod(str, &ptr); + } else if (dp_or_e != -1) { + dp_or_e = (*ptr++ == '.') ? 1 : 2; + goto check_digits; + } + } else { + return IS_NULL; + } + if (ptr != str + length) { + zend_error(E_NOTICE, "A non well formed numeric value encountered"); + return 0; + } + + if (type == IS_LONG) { + if (digits == MAX_LENGTH_OF_INT64 - 1) { + int cmp = strcmp(&ptr[-digits], int64_min_digits); + + if (!(cmp < 0 || (cmp == 0 && *str == '-'))) { + if (dval) { + *dval = zend_strtod(str, NULL); + } + + return IS_DOUBLE; + } + } + if (lval) { + *lval = strtoll(str, NULL, base); + } + return IS_LONG; + } else { + if (dval) { + *dval = local_dval; + } + return IS_DOUBLE; + } +} + #define CONVERT_TO_INTEGER(type) \ - static bool convert_long_to_##type(long val, type##_t* type##_value) { \ + static bool convert_int64_to_##type(int64_t val, type##_t* type##_value) { \ *type##_value = (type##_t)val; \ return true; \ } \ @@ -91,15 +212,15 @@ void util_init(TSRMLS_D) { \ static bool convert_string_to_##type(const char* val, int len, \ type##_t* type##_value) { \ - long lval; \ + int64_t lval; \ double dval; \ \ - switch (is_numeric_string(val, len, &lval, &dval, 0)) { \ + switch (convert_numeric_string(val, len, &lval, &dval)) { \ case IS_DOUBLE: { \ return convert_double_to_##type(dval, type##_value); \ } \ case IS_LONG: { \ - return convert_long_to_##type(lval, type##_value); \ + return convert_int64_to_##type(lval, type##_value); \ } \ default: \ zend_error(E_USER_ERROR, \ @@ -111,7 +232,7 @@ void util_init(TSRMLS_D) { bool protobuf_convert_to_##type(zval* from, type##_t* to) { \ switch (Z_TYPE_P(from)) { \ case IS_LONG: { \ - return convert_long_to_##type(Z_LVAL_P(from), to); \ + return convert_int64_to_##type(Z_LVAL_P(from), to); \ } \ case IS_DOUBLE: { \ return convert_double_to_##type(Z_DVAL_P(from), to); \ @@ -137,7 +258,7 @@ CONVERT_TO_INTEGER(uint64); #undef CONVERT_TO_INTEGER #define CONVERT_TO_FLOAT(type) \ - static bool convert_long_to_##type(long val, type* type##_value) { \ + static bool convert_int64_to_##type(int64_t val, type* type##_value) { \ *type##_value = (type)val; \ return true; \ } \ @@ -149,10 +270,10 @@ CONVERT_TO_INTEGER(uint64); \ static bool convert_string_to_##type(const char* val, int len, \ type* type##_value) { \ - long lval; \ + int64_t lval; \ double dval; \ \ - switch (is_numeric_string(val, len, &lval, &dval, 0)) { \ + switch (convert_numeric_string(val, len, &lval, &dval)) { \ case IS_DOUBLE: { \ *type##_value = (type)dval; \ return true; \ @@ -171,7 +292,7 @@ CONVERT_TO_INTEGER(uint64); bool protobuf_convert_to_##type(zval* from, type* to) { \ switch (Z_TYPE_P(from)) { \ case IS_LONG: { \ - return convert_long_to_##type(Z_LVAL_P(from), to); \ + return convert_int64_to_##type(Z_LVAL_P(from), to); \ } \ case IS_DOUBLE: { \ return convert_double_to_##type(Z_DVAL_P(from), to); \ |