Fix bugs. Refactor code. Improve performance of `Pair`.
This commit is contained in:
parent
f26e503772
commit
639479d031
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -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--
|
Reference in New Issue