Fix bugs. Refactor code. Improve performance of `Pair`.

This commit is contained in:
CismonX 2018-08-31 01:04:46 +08:00
parent f26e503772
commit 639479d031
4 changed files with 166 additions and 56 deletions

View File

@ -19,18 +19,10 @@ zend_object_handlers collection_handlers;
zend_class_entry* collections_collection_ce;
zend_class_entry* collections_pair_ce;
zend_string* collections_pair_first;
zend_string* collections_pair_second;
ZEND_DECLARE_MODULE_GLOBALS(collections)
PHP_MINIT_FUNCTION(collections)
static zend_always_inline void collection_ce_init()
{
#ifdef ZTS
ZEND_INIT_MODULE_GLOBALS(collections, NULL, NULL);
#endif
collections_pair_first = zend_string_init("first", sizeof "first" - 1, 1);
collections_pair_second = zend_string_init("second", sizeof "second" - 1, 1);
zend_class_entry collection_ce;
INIT_CLASS_ENTRY_EX(collection_ce, "Collection", sizeof "Collection" - 1, collection_methods);
collections_collection_ce = zend_register_internal_class(&collection_ce);
@ -46,14 +38,33 @@ PHP_MINIT_FUNCTION(collections)
#endif
zend_ce_arrayaccess);
memcpy(&collection_handlers, &std_object_handlers, sizeof(zend_object_handlers));
collection_handlers.count_elements = count_collection;
collection_handlers.unset_dimension = collection_offset_unset;
collection_handlers.unset_property = collection_property_unset;
collection_handlers.write_dimension = collection_offset_set;
collection_handlers.write_property = collection_property_set;
collection_handlers.read_dimension = collection_offset_get;
collection_handlers.read_property = collection_property_get;
collection_handlers.has_dimension = collection_offset_exists;
collection_handlers.has_property = collection_property_exists;
collection_handlers.count_elements = count_collection;
}
static zend_always_inline void pair_ce_init()
{
zend_class_entry pair_ce;
INIT_CLASS_ENTRY_EX(pair_ce, "Pair", sizeof "Pair" - 1, pair_methods);
collections_pair_ce = zend_register_internal_class(&pair_ce);
zend_declare_property_null(collections_pair_ce, "first", sizeof "first" - 1, ZEND_ACC_PUBLIC);
zend_declare_property_null(collections_pair_ce, "second", sizeof "second" - 1, ZEND_ACC_PUBLIC);
}
PHP_MINIT_FUNCTION(collections)
{
#ifdef ZTS
ZEND_INIT_MODULE_GLOBALS(collections, NULL, NULL);
#endif
collection_ce_init();
pair_ce_init();
return SUCCESS;
}

View File

@ -12,41 +12,23 @@
#include "php_collections_me.h"
#if PHP_VERSION_ID < 70300
#define GC_ADDREF(p) ++GC_REFCOUNT(p)
#define GC_DELREF(p) --GC_REFCOUNT(p)
#define GC_ADDREF(p) ++GC_REFCOUNT(p)
#define GC_DELREF(p) --GC_REFCOUNT(p)
#endif
#define FCI_G COLLECTIONS_G(fci)
#define FCC_G COLLECTIONS_G(fcc)
#define FCI_G COLLECTIONS_G(fci)
#define FCC_G COLLECTIONS_G(fcc)
#define NEW_OBJ(name, ce, object_handlers) \
zend_object* (name) = (zend_object*)ecalloc(1, sizeof(zend_object) + \
zend_object_properties_size(ce)); \
zend_object_std_init(name, ce); \
(name)->handlers = object_handlers
#define NEW_COLLECTION_OBJ(name) \
NEW_OBJ(name, collections_collection_ce, &collection_handlers)
#define NEW_PAIR_OBJ(name) \
NEW_OBJ(name, collections_pair_ce, &std_object_handlers); \
name->properties = (zend_array*)emalloc(sizeof(zend_array)); \
zend_hash_init(name->properties, 2, NULL, ZVAL_PTR_DTOR, 0)
#define COLLECTION_FETCH(val) (Z_OBJ_P(val)->properties)
#define COLLECTION_FETCH_CURRENT() COLLECTION_FETCH(getThis())
#define PAIR_FETCH_FIRST(obj) OBJ_PROP_NUM(obj, 0)
#define PAIR_FETCH_SECOND(obj) OBJ_PROP_NUM(obj, 1)
#define IS_COLLECTION_P(zval) \
Z_TYPE_P(zval) == IS_OBJECT && Z_OBJCE_P(zval) == collections_collection_ce
#define IS_PAIR(zval) \
EXPECTED(Z_TYPE(zval) == IS_OBJECT) && EXPECTED(Z_OBJCE(zval) == collections_pair_ce)
#define OBJ_PROPERTY_UPDATE(obj, property_name, value) \
zend_hash_update((obj)->properties, property_name, value)
#define OBJ_PROPERTY_FETCH(obj, property_name) \
zend_hash_find((obj)->properties, property_name)
#define PAIR_UPDATE_FIRST(obj, value) OBJ_PROPERTY_UPDATE(obj, collections_pair_first, value)
#define PAIR_UPDATE_SECOND(obj, value) OBJ_PROPERTY_UPDATE(obj, collections_pair_second, value)
#define PAIR_FETCH_FIRST(obj) OBJ_PROPERTY_FETCH(obj, collections_pair_first)
#define PAIR_FETCH_SECOND(obj) OBJ_PROPERTY_FETCH(obj, collections_pair_second)
#define COLLECTION_FETCH(obj) Z_OBJ_P(obj)->properties
#define COLLECTION_FETCH_CURRENT() COLLECTION_FETCH(getThis())
#define SEPARATE_COLLECTION(ht, obj) \
if (GC_REFCOUNT(ht) > 1) \
{ \
@ -118,7 +100,7 @@
#define RETVAL_NEW_COLLECTION(collection) \
do \
{ \
NEW_COLLECTION_OBJ(obj); \
zend_object* obj = create_collection_obj(); \
if (GC_REFCOUNT(collection) > 1) \
GC_ADDREF(collection); \
obj->properties = collection; \
@ -136,6 +118,39 @@ typedef int (*equal_check_func_t)(zval*, zval*);
/// Unused global variable.
zval rv;
static zend_always_inline void pair_update_first(zend_object* obj, zval* value)
{
zval_ptr_dtor(PAIR_FETCH_FIRST(obj));
ZVAL_COPY_VALUE(PAIR_FETCH_FIRST(obj), value);
}
static zend_always_inline void pair_update_second(zend_object* obj, zval* value)
{
zval_ptr_dtor(PAIR_FETCH_SECOND(obj));
ZVAL_COPY_VALUE(PAIR_FETCH_SECOND(obj), value);
}
static zend_always_inline zend_object* create_object(zend_class_entry* ce,
zend_object_handlers* handlers)
{
zend_object* obj = (zend_object*)ecalloc(1, sizeof(zend_object) +
zend_object_properties_size(ce));
zend_object_std_init(obj, ce);
object_properties_init(obj, ce);
obj->handlers = handlers;
return obj;
}
static zend_always_inline zend_object* create_collection_obj()
{
return create_object(collections_collection_ce, &collection_handlers);
}
static zend_always_inline zend_object* create_pair_obj()
{
return create_object(collections_pair_ce, &std_object_handlers);
}
static zend_always_inline void bucket_to_pair(zend_object* pair, Bucket* bucket)
{
zval key;
@ -148,8 +163,8 @@ static zend_always_inline void bucket_to_pair(zend_object* pair, Bucket* bucket)
{
ZVAL_LONG(&key, bucket->h);
}
PAIR_UPDATE_FIRST(pair, &key);
PAIR_UPDATE_SECOND(pair, &bucket->val);
pair_update_first(pair, &key);
pair_update_second(pair, &bucket->val);
}
static int bucket_compare_numeric(const void* op1, const void* op2)
@ -448,6 +463,37 @@ int collection_offset_exists(zval* object, zval* offset, int check_empty)
return 0;
}
int collection_property_exists(zval* object, zval* member, int has_set_exists,
void** unused)
{
zend_array* current = COLLECTION_FETCH(object);
zval* found = NULL;
if (EXPECTED(Z_TYPE_P(member) == IS_STRING))
{
found = zend_hash_find(current, Z_STR_P(member));
}
else if (EXPECTED(Z_TYPE_P(member) == IS_LONG))
{
found = zend_hash_index_find(current, Z_LVAL_P(member));
}
if (found == NULL)
{
return 0;
}
// whether property exists and is not NULL
if (has_set_exists == 0)
{
return Z_TYPE_P(found) != IS_NULL;
}
// whether property exists and is true
else if (has_set_exists == 1)
{
return zend_is_true(found);
}
// whether property exists
return 1;
}
void collection_offset_set(zval* object, zval* offset, zval* value)
{
zend_array* current = COLLECTION_FETCH(object);
@ -464,10 +510,15 @@ void collection_offset_set(zval* object, zval* offset, zval* value)
{
ERR_BAD_KEY_TYPE();
return;
}
}
Z_TRY_ADDREF_P(value);
}
void collection_property_set(zval* object, zval* member, zval* value, void** unused)
{
collection_offset_set(object, member, value);
}
zval* collection_offset_get(zval* object, zval* offset, int type, zval* retval)
{
// Note that we don't handle type. So don't do any fancy things with Collection
@ -482,10 +533,23 @@ zval* collection_offset_get(zval* object, zval* offset, int type, zval* retval)
{
found = zend_hash_find(current, Z_STR_P(offset));
}
ZVAL_COPY(retval, found);
if (found)
{
ZVAL_COPY_VALUE(retval, found);
}
else
{
retval = &EG(uninitialized_zval);
}
return retval;
}
zval* collection_property_get(zval* object, zval* member, int type, void** unused,
zval* retval)
{
return collection_offset_get(object, member, type, retval);
}
void collection_offset_unset(zval* object, zval* offset)
{
zend_array* current = COLLECTION_FETCH(object);
@ -500,6 +564,11 @@ void collection_offset_unset(zval* object, zval* offset)
}
}
void collection_property_unset(zval* object, zval* member, void** unused)
{
collection_offset_unset(object, member);
}
PHP_METHOD(Collection, __construct) {}
PHP_METHOD(Collection, addAll)
@ -1769,7 +1838,7 @@ PHP_METHOD(Collection, maxWith)
zend_array* current = COLLECTION_FETCH_CURRENT();
ARRAY_CLONE(max_with, current);
ZEND_HASH_FOREACH_BUCKET(max_with, Bucket* bucket)
NEW_PAIR_OBJ(obj);
zend_object* obj = create_pair_obj();
bucket_to_pair(obj, bucket);
ZVAL_OBJ(&bucket->val, obj);
ZEND_HASH_FOREACH_END();
@ -1853,7 +1922,7 @@ PHP_METHOD(Collection, minWith)
zend_array* current = COLLECTION_FETCH_CURRENT();
ARRAY_CLONE(min_with, current);
ZEND_HASH_FOREACH_BUCKET(min_with, Bucket* bucket)
NEW_PAIR_OBJ(obj);
zend_object* obj = create_pair_obj();
bucket_to_pair(obj, bucket);
ZVAL_OBJ(&bucket->val, obj);
ZEND_HASH_FOREACH_END();
@ -1949,7 +2018,7 @@ PHP_METHOD(Collection, partition)
ZEND_PARSE_PARAMETERS_END();
INIT_FCI(&fci, 2);
zend_array* current = COLLECTION_FETCH_CURRENT();
NEW_PAIR_OBJ(pair);
zend_object* pair = create_pair_obj();
ARRAY_NEW_EX(first_arr, current);
ARRAY_NEW_EX(second_arr, current);
ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket)
@ -1973,8 +2042,8 @@ PHP_METHOD(Collection, partition)
zval first, second;
ZVAL_ARR(&first, first_arr);
ZVAL_ARR(&second, second_arr);
PAIR_UPDATE_FIRST(pair, &first);
PAIR_UPDATE_SECOND(pair, &second);
pair_update_first(pair, &first);
pair_update_second(pair, &second);
RETVAL_OBJ(pair);
}
@ -2519,7 +2588,7 @@ PHP_METHOD(Collection, sortWith)
zend_array* current = COLLECTION_FETCH_CURRENT();
ARRAY_CLONE(sorted_with, current);
ZEND_HASH_FOREACH_BUCKET(sorted_with, Bucket* bucket)
NEW_PAIR_OBJ(obj);
zend_object* obj = create_pair_obj();
bucket_to_pair(obj, bucket);
ZVAL_OBJ(&bucket->val, obj);
ZEND_HASH_FOREACH_END();
@ -2672,7 +2741,7 @@ PHP_METHOD(Collection, sortedWith)
zend_array* current = COLLECTION_FETCH_CURRENT();
ARRAY_CLONE(sorted_with, current);
ZEND_HASH_FOREACH_BUCKET(sorted_with, Bucket* bucket)
NEW_PAIR_OBJ(obj);
zend_object* obj = create_pair_obj();
bucket_to_pair(obj, bucket);
ZVAL_OBJ(&bucket->val, obj);
ZEND_HASH_FOREACH_END();
@ -2903,7 +2972,7 @@ PHP_METHOD(Collection, toPairs)
zend_array* current = COLLECTION_FETCH_CURRENT();
ARRAY_NEW_EX(new_collection, current);
ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket)
NEW_PAIR_OBJ(obj);
zend_object* obj = create_pair_obj();
bucket_to_pair(obj, bucket);
zval pair;
ZVAL_OBJ(&pair, obj);
@ -2935,8 +3004,8 @@ PHP_METHOD(Pair, __construct)
Z_PARAM_ZVAL(first)
Z_PARAM_ZVAL(second)
ZEND_PARSE_PARAMETERS_END();
zend_array* properties = Z_OBJ_P(getThis())->properties = (zend_array*)emalloc(sizeof(zend_array));
zend_hash_init(properties, 2, NULL, ZVAL_PTR_DTOR, 0);
PAIR_UPDATE_FIRST(Z_OBJ_P(getThis()), first);
PAIR_UPDATE_SECOND(Z_OBJ_P(getThis()), second);
Z_TRY_ADDREF_P(first);
Z_TRY_ADDREF_P(second);
pair_update_first(Z_OBJ_P(getThis()), first);
pair_update_second(Z_OBJ_P(getThis()), second);
}

View File

@ -48,9 +48,6 @@ ZEND_TSRMLS_CACHE_EXTERN()
extern PHP_COLLECTIONS_API zend_class_entry* collections_collection_ce;
extern PHP_COLLECTIONS_API zend_class_entry* collections_pair_ce;
extern zend_string* collections_pair_first;
extern zend_string* collections_pair_second;
extern zend_object_handlers collection_handlers;
int count_collection(zval* obj, zend_long* count);
@ -58,6 +55,10 @@ int collection_offset_exists(zval* object, zval* offset, int check_empty);
void collection_offset_set(zval* object, zval* offset, zval* value);
zval* collection_offset_get(zval* object, zval* offset, int type, zval* retval);
void collection_offset_unset(zval* object, zval* offset);
int collection_property_exists(zval* object, zval* member, int has_set_exists, void**);
void collection_property_set(zval* object, zval* member, zval* value, void**);
zval* collection_property_get(zval* object, zval* member, int type, void**, zval* retval);
void collection_property_unset(zval* object, zval* member, void**);
extern const zend_function_entry collection_methods[];
extern const zend_function_entry pair_methods[];

View File

@ -0,0 +1,29 @@
--TEST--
Test accessing elements of Collection as object properties.
--FILE--
<?php
$collection = Collection::init([
'a' => 'b',
'c' => 'd',
'e' => 0,
'f' => null
]);
if (!property_exists($collection, 'e') || !property_exists($collection, 'f') ||
!isset($collection->e) || isset($collection->f) ||
!empty($collection->e) || !empty($collection->f)
) {
echo 'Test for handlers.has_property failed.', PHP_EOL;
}
if ($collection->a != 'b') {
echo 'Test for handlers.read_property failed.', PHP_EOL;
}
$collection->c = 'g';
if ($collection->c != 'g') {
echo 'Test for handlers.write_property failed.', PHP_EOL;
}
unset($collection->e);
if (isset($collection->e)) {
echo 'Test for handlers.unset_property failed.', PHP_EOL;
}
?>
--EXPECT--