diff --git a/src/collections_methods.c b/src/collections_methods.c index 65fdd61..3a77a09 100644 --- a/src/collections_methods.c +++ b/src/collections_methods.c @@ -10,26 +10,26 @@ #include "php_collections.h" #include "php_collections_me.h" -#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); \ +#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) \ +#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)); \ +#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 IS_COLLECTION_P(zval) \ +#define IS_COLLECTION_P(zval) \ Z_TYPE_P(zval) == IS_OBJECT && Z_OBJCE_P(zval) == collections_collection_ce -#define IS_PAIR(zval) \ +#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) \ +#define OBJ_PROPERTY_UPDATE(obj, property_name, value) \ zend_hash_update((obj)->properties, property_name, value) -#define OBJ_PROPERTY_FETCH(obj, property_name) \ +#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) @@ -38,34 +38,39 @@ #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) { \ - GC_DELREF(ht); \ - ht = Z_OBJ_P(obj)->properties = zend_array_dup(ht); \ +#define SEPARATE_COLLECTION(ht, obj) \ + if (GC_REFCOUNT(ht) > 1) \ + { \ + GC_DELREF(ht); \ + ht = Z_OBJ_P(obj)->properties = zend_array_dup(ht); \ } #define SEPARATE_CURRENT_COLLECTION(ht) SEPARATE_COLLECTION(ht, getThis()) -#define INIT_FCI(num_args) \ - zval params[num_args], retval; \ - fci.param_count = num_args; \ - fci.retval = &retval; \ +#define INIT_FCI(num_args) \ + zval params[num_args], retval; \ + fci.param_count = num_args; \ + fci.retval = &retval; \ fci.params = params; -#define CALLBACK_KEYVAL_INVOKE(params, bucket) \ - ZVAL_COPY_VALUE(¶ms[0], &bucket->val); \ - if ((bucket)->key) \ - ZVAL_STR(¶ms[1], (bucket)->key); \ - else \ - ZVAL_LONG(¶ms[1], (bucket)->h); \ +#define CALLBACK_KEYVAL_INVOKE(params, bucket) \ + ZVAL_COPY_VALUE(¶ms[0], &bucket->val); \ + if ((bucket)->key) \ + { \ + ZVAL_STR(¶ms[1], (bucket)->key); \ + } \ + else \ + { \ + ZVAL_LONG(¶ms[1], (bucket)->h); \ + } \ zend_call_function(&fci, &fcc) -#define INIT_EQUAL_CHECK_FUNC(val) \ - int (*equal_check_func)(zval*, zval*); \ - if (Z_TYPE_P(val) == IS_LONG) \ - equal_check_func = fast_equal_check_long; \ - else if (Z_TYPE_P(val) == IS_STRING) \ - equal_check_func = fast_equal_check_string; \ - else \ +#define INIT_EQUAL_CHECK_FUNC(val) \ + int (*equal_check_func)(zval*, zval*); \ + if (Z_TYPE_P(val) == IS_LONG) \ + equal_check_func = fast_equal_check_long; \ + else if (Z_TYPE_P(val) == IS_STRING) \ + equal_check_func = fast_equal_check_string; \ + else \ equal_check_func = fast_equal_check_function; #define PHP_COLLECTIONS_ERROR(type, msg) php_error_docref(NULL, type, msg) @@ -77,38 +82,45 @@ #define ERR_NOT_ARITHMETIC() PHP_COLLECTIONS_ERROR(E_WARNING, "Elements should be int or double") #define ERR_SILENCED() -#define ELEMENTS_VALIDATE(elements, err, err_then) \ - zend_array* elements##_arr; \ - if (IS_COLLECTION_P(elements)) \ - (elements##_arr) = COLLECTION_FETCH(elements); \ - else if (UNEXPECTED(Z_TYPE_P(elements) == IS_ARRAY))\ - (elements##_arr) = Z_ARRVAL_P(elements); \ - else { \ - err(); \ - err_then; \ +#define ELEMENTS_VALIDATE(elements, err, err_then) \ + zend_array* elements##_arr; \ + if (IS_COLLECTION_P(elements)) \ + { \ + (elements##_arr) = COLLECTION_FETCH(elements); \ + } \ + else if (UNEXPECTED(Z_TYPE_P(elements) == IS_ARRAY)) \ + { \ + (elements##_arr) = Z_ARRVAL_P(elements); \ + } \ + else \ + { \ + err(); \ + err_then; \ } -#define ARRAY_NEW(name, size) \ - zend_array* (name) = (zend_array*)emalloc(sizeof(zend_array)); \ +#define ARRAY_NEW(name, size) \ + zend_array* (name) = (zend_array*)emalloc(sizeof(zend_array)); \ zend_hash_init(name, size, NULL, ZVAL_PTR_DTOR, 0) -#define ARRAY_NEW_EX(name, other) \ +#define ARRAY_NEW_EX(name, other) \ ARRAY_NEW(name, zend_hash_num_elements(other)) -#define ARRAY_CLONE(dest, src) \ - ARRAY_NEW_EX(dest, src); \ +#define ARRAY_CLONE(dest, src) \ + ARRAY_NEW_EX(dest, src); \ zend_hash_copy(dest, src, NULL) -#define RETVAL_NEW_COLLECTION(collection) \ - do { \ - NEW_COLLECTION_OBJ(obj); \ - if (GC_REFCOUNT(collection) > 1) \ - GC_ADDREF(collection); \ - obj->properties = collection; \ - RETVAL_OBJ(obj); \ +#define RETVAL_NEW_COLLECTION(collection) \ + do \ + { \ + NEW_COLLECTION_OBJ(obj); \ + if (GC_REFCOUNT(collection) > 1) \ + GC_ADDREF(collection); \ + obj->properties = collection; \ + RETVAL_OBJ(obj); \ } while (0) -#define RETURN_NEW_COLLECTION(collection) { \ - RETVAL_NEW_COLLECTION(collection); \ - return; \ +#define RETURN_NEW_COLLECTION(collection) \ + { \ + RETVAL_NEW_COLLECTION(collection); \ + return; \ } /// Unused global variable. @@ -134,7 +146,9 @@ static zend_always_inline int bucket_compare_regular(const void* op1, const void Bucket* b2 = (Bucket*)op2; zval result; if (compare_function(&result, &b1->val, &b2->val) == FAILURE) + { return 0; + } return ZEND_NORMALIZE_BOOL(Z_LVAL(result)); } @@ -149,11 +163,17 @@ int collection_offset_exists(zval* object, zval* offset, int check_empty) { zend_array* current = COLLECTION_FETCH(object); if (check_empty) + { return zend_hash_num_elements(current) == 0; + } if (Z_TYPE_P(offset) == IS_LONG) + { return zend_hash_index_exists(current, Z_LVAL_P(offset)); + } if (Z_TYPE_P(offset) == IS_STRING) + { return zend_hash_exists(current, Z_STR_P(offset)); + } return 0; } @@ -162,9 +182,13 @@ void collection_offset_set(zval* object, zval* offset, zval* value) zend_array* current = COLLECTION_FETCH(object); SEPARATE_COLLECTION(current, object); if (Z_TYPE_P(offset) == IS_LONG) + { zend_hash_index_update(current, Z_LVAL_P(offset), value); + } else if (Z_TYPE_P(offset) == IS_STRING) + { zend_hash_update(current, Z_STR_P(offset), value); + } } zval* collection_offset_get(zval* object, zval* offset, int type, zval* retval) @@ -174,9 +198,13 @@ zval* collection_offset_get(zval* object, zval* offset, int type, zval* retval) zend_array* current = COLLECTION_FETCH(object); zval* found = NULL; if (Z_TYPE_P(offset) == IS_LONG) + { found = zend_hash_index_find(current, Z_LVAL_P(offset)); + } else if (Z_TYPE_P(offset) == IS_STRING) + { found = zend_hash_find(current, Z_STR_P(offset)); + } ZVAL_COPY(retval, found); return retval; } @@ -186,9 +214,13 @@ void collection_offset_unset(zval* object, zval* offset) zend_array* current = COLLECTION_FETCH(object); SEPARATE_COLLECTION(current, object); if (Z_TYPE_P(offset) == IS_LONG) + { zend_hash_index_del(current, Z_LVAL_P(offset)); + } else if (Z_TYPE_P(offset) == IS_STRING) + { zend_hash_del(current, Z_STR_P(offset)); + } } PHP_METHOD(Collection, __construct) {} @@ -205,11 +237,17 @@ PHP_METHOD(Collection, addAll) ZEND_HASH_FOREACH_BUCKET(elements_arr, Bucket* bucket) Z_TRY_ADDREF(bucket->val); if (bucket->key) + { zend_hash_add(current, bucket->key, &bucket->val); + } else if (HT_IS_PACKED(elements_arr)) + { zend_hash_next_index_insert(current, &bucket->val); + } else + { zend_hash_index_add(current, bucket->h, &bucket->val); + } ZEND_HASH_FOREACH_END(); } @@ -224,7 +262,8 @@ PHP_METHOD(Collection, all) zend_array* current = COLLECTION_FETCH_CURRENT(); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (!zend_is_true(&retval)) { + if (!zend_is_true(&retval)) + { zval_ptr_dtor(&retval); RETURN_FALSE; } @@ -244,7 +283,8 @@ PHP_METHOD(Collection, any) zend_array* current = COLLECTION_FETCH_CURRENT(); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (zend_is_true(&retval)) { + if (zend_is_true(&retval)) + { zval_ptr_dtor(&retval); RETURN_TRUE; } @@ -265,19 +305,31 @@ PHP_METHOD(Collection, associate) ARRAY_NEW_EX(new_collection, current); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (IS_PAIR(retval)) { + if (IS_PAIR(retval)) + { zval* key = PAIR_FETCH_FIRST(Z_OBJ(retval)); zval* value = PAIR_FETCH_SECOND(Z_OBJ(retval)); if (Z_TYPE_P(key) == IS_LONG) + { zend_hash_index_add(new_collection, Z_LVAL_P(key), value); + } else if (Z_TYPE_P(key) == IS_STRING) + { zend_hash_add(new_collection, Z_STR_P(key), value); + } else if (Z_TYPE_P(key) == IS_NULL) + { zend_hash_next_index_insert(new_collection, value); + } else + { ERR_BAD_KEY_TYPE(); - } else + } + } + else + { ERR_BAD_CALLBACK_RETVAL(); + } zval_ptr_dtor(&retval); ZEND_HASH_FOREACH_END(); RETVAL_NEW_COLLECTION(new_collection); @@ -298,19 +350,31 @@ PHP_METHOD(Collection, associateTo) SEPARATE_COLLECTION(dest_arr, dest); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (IS_PAIR(retval)) { + if (IS_PAIR(retval)) + { zval* key = PAIR_FETCH_FIRST(Z_OBJ(retval)); zval* value = PAIR_FETCH_SECOND(Z_OBJ(retval)); if (Z_TYPE_P(key) == IS_LONG) + { zend_hash_index_add(dest_arr, Z_LVAL_P(key), value); + } else if (Z_TYPE_P(key) == IS_STRING) + { zend_hash_add(dest_arr, Z_STR_P(key), value); + } else if (Z_TYPE_P(key) == IS_NULL) + { zend_hash_next_index_insert(dest_arr, value); + } else + { ERR_BAD_KEY_TYPE(); - } else + } + } + else + { ERR_BAD_CALLBACK_RETVAL(); + } zval_ptr_dtor(&retval); ZEND_HASH_FOREACH_END(); RETVAL_ZVAL(dest, 1, 0); @@ -329,11 +393,17 @@ PHP_METHOD(Collection, associateBy) ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); if (Z_TYPE(retval) == IS_LONG) + { zend_hash_index_add(new_collection, Z_LVAL(retval), &bucket->val); + } else if (Z_TYPE(retval) == IS_STRING) + { zend_hash_add(new_collection, Z_STR(retval), &bucket->val); + } else + { ERR_BAD_CALLBACK_RETVAL(); + } zval_ptr_dtor(&retval); ZEND_HASH_FOREACH_END(); RETVAL_NEW_COLLECTION(new_collection); @@ -355,11 +425,17 @@ PHP_METHOD(Collection, associateByTo) ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); if (Z_TYPE(retval) == IS_LONG) + { zend_hash_index_add(dest_arr, Z_LVAL(retval), &bucket->val); + } else if (Z_TYPE(retval) == IS_STRING) + { zend_hash_add(dest_arr, Z_STR(retval), &bucket->val); + } else + { ERR_BAD_CALLBACK_RETVAL(); + } zval_ptr_dtor(&retval); ZEND_HASH_FOREACH_END(); RETVAL_ZVAL(dest, 1, 0); @@ -371,10 +447,15 @@ PHP_METHOD(Collection, average) double sum = 0; ZEND_HASH_FOREACH_VAL(current, zval* val) if (Z_TYPE_P(val) == IS_LONG) + { sum += Z_LVAL_P(val); + } else if (Z_TYPE_P(val) == IS_DOUBLE) + { sum += Z_DVAL_P(val); - else { + } + else + { ERR_NOT_ARITHMETIC(); return; } @@ -394,11 +475,16 @@ PHP_METHOD(Collection, containsAll) INIT_EQUAL_CHECK_FUNC(element); int result = 0; ZEND_HASH_FOREACH_VAL(current, zval* val) - if (result = equal_check_func(element, val)) + result = equal_check_func(element, val); + if (result) + { break; + } ZEND_HASH_FOREACH_END(); if (result == 0) + { RETURN_FALSE; + } ZEND_HASH_FOREACH_END(); RETURN_TRUE; } @@ -411,9 +497,13 @@ PHP_METHOD(Collection, containsKey) ZEND_PARSE_PARAMETERS_END(); zend_array* current = COLLECTION_FETCH_CURRENT(); if (Z_TYPE_P(key) == IS_LONG) + { RETURN_BOOL(zend_hash_index_exists(current, Z_LVAL_P(key))); + } if (Z_TYPE_P(key) == IS_STRING) + { RETURN_BOOL(zend_hash_exists(current, Z_STR_P(key))); + } ERR_BAD_KEY_TYPE(); RETVAL_FALSE; } @@ -428,7 +518,9 @@ PHP_METHOD(Collection, containsValue) INIT_EQUAL_CHECK_FUNC(element); ZEND_HASH_FOREACH_VAL(current, zval* val) if (equal_check_func(element, val)) + { RETURN_TRUE; + } ZEND_HASH_FOREACH_END(); RETURN_FALSE; } @@ -441,26 +533,35 @@ PHP_METHOD(Collection, copyOf) Z_PARAM_LONG(new_size); ZEND_PARSE_PARAMETERS_END(); zend_array* current = COLLECTION_FETCH_CURRENT(); - if (EX_NUM_ARGS() == 0) { + if (EX_NUM_ARGS() == 0) + { ARRAY_CLONE(new_collection, current); RETURN_NEW_COLLECTION(new_collection); } - if (UNEXPECTED(new_size < 0)) { + if (UNEXPECTED(new_size < 0)) + { ERR_BAD_SIZE(); RETURN_NULL(); } - if (new_size == 0) { + if (new_size == 0) + { ARRAY_NEW(new_collection, 0); RETURN_NEW_COLLECTION(new_collection); } ARRAY_NEW_EX(new_collection, current); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) if (bucket->key) + { zend_hash_add_new(new_collection, bucket->key, &bucket->val); + } else + { zend_hash_index_add_new(new_collection, bucket->h, &bucket->val); + } if (--new_size == 0) + { break; + } ZEND_HASH_FOREACH_END(); RETVAL_NEW_COLLECTION(new_collection); } @@ -472,11 +573,13 @@ PHP_METHOD(Collection, copyOfRange) Z_PARAM_LONG(from_idx) Z_PARAM_LONG(num_elements) ZEND_PARSE_PARAMETERS_END(); - if (from_idx < 0) { + if (from_idx < 0) + { ERR_BAD_INDEX(); RETURN_NULL(); } - if (num_elements < 0) { + if (num_elements < 0) + { ERR_BAD_SIZE(); RETURN_NULL(); } @@ -486,14 +589,22 @@ PHP_METHOD(Collection, copyOfRange) Bucket* end = bucket + current->nNumUsed; for (bucket += from_idx; num_elements > 0 && bucket < end; ++bucket) { if (Z_ISUNDEF(bucket->val)) + { continue; + } --num_elements; if (bucket->key) + { zend_hash_add(new_collection, bucket->key, &bucket->val); + } else if (HT_IS_PACKED(current)) + { zend_hash_next_index_insert(new_collection, &bucket->val); + } else + { zend_hash_index_add(new_collection, bucket->h, &bucket->val); + } } RETVAL_NEW_COLLECTION(new_collection); } @@ -521,7 +632,8 @@ PHP_METHOD(Collection, drop) ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_LONG(n) ZEND_PARSE_PARAMETERS_END(); - if (n < 0) { + if (n < 0) + { ERR_BAD_SIZE(); return; } @@ -529,9 +641,12 @@ PHP_METHOD(Collection, drop) ARRAY_CLONE(new_collection, current); Bucket* bucket = new_collection->arData; Bucket* end = bucket + new_collection->nNumUsed; - for (; n > 0 && bucket < end; ++bucket) { + for (; n > 0 && bucket < end; ++bucket) + { if (Z_ISUNDEF(bucket->val)) + { continue; + } --n; Z_TRY_ADDREF(bucket->val); zend_hash_del_bucket(new_collection, bucket); @@ -545,17 +660,21 @@ PHP_METHOD(Collection, dropLast) ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_LONG(n) ZEND_PARSE_PARAMETERS_END(); - if (n < 0) { + if (n < 0) + { ERR_BAD_SIZE(); return; } zend_array* current = COLLECTION_FETCH_CURRENT(); ARRAY_CLONE(new_collection, current); unsigned idx = new_collection->nNumUsed; - for (; n > 0 && idx > 0; --idx) { + for (; n > 0 && idx > 0; --idx) + { Bucket* bucket = new_collection->arData + idx - 1; if (Z_ISUNDEF(bucket->val)) + { continue; + } --n; Z_TRY_ADDREF(bucket->val); zend_hash_del_bucket(new_collection, bucket); @@ -575,10 +694,13 @@ PHP_METHOD(Collection, dropLastWhile) ARRAY_CLONE(new_collection, current); ZEND_HASH_REVERSE_FOREACH_BUCKET(new_collection, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (zend_is_true(&retval)) { + if (zend_is_true(&retval)) + { zval_ptr_dtor(&retval); zend_hash_del_bucket(new_collection, bucket); - } else { + } + else + { zval_ptr_dtor(&retval); break; } @@ -598,10 +720,13 @@ PHP_METHOD(Collection, dropWhile) ARRAY_CLONE(new_collection, current); ZEND_HASH_FOREACH_BUCKET(new_collection, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (zend_is_true(&retval)) { + if (zend_is_true(&retval)) + { zval_ptr_dtor(&retval); zend_hash_del_bucket(new_collection, bucket); - } else { + } + else + { zval_ptr_dtor(&retval); break; } @@ -624,13 +749,20 @@ PHP_METHOD(Collection, fill) ZEND_PARSE_PARAMETERS_END(); Bucket* bucket = current->arData; Bucket* end = bucket + current->nNumUsed; - for (bucket += from_idx; num_elements > 0 && bucket < end; ++bucket, --num_elements) { + for (bucket += from_idx; num_elements > 0 && bucket < end; ++bucket, --num_elements) + { if (Z_ISUNDEF(bucket->val)) + { continue; + } if (bucket->key) + { zend_hash_update(current, bucket->key, element); + } else + { zend_hash_index_update(current, bucket->h, element); + } } } @@ -646,13 +778,20 @@ PHP_METHOD(Collection, filter) ARRAY_NEW_EX(new_collection, current); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (zend_is_true(&retval)) { + if (zend_is_true(&retval)) + { if (bucket->key) + { zend_hash_add(new_collection, bucket->key, &bucket->val); + } else if (HT_IS_PACKED(current)) + { zend_hash_next_index_insert(new_collection, &bucket->val); + } else + { zend_hash_index_add(new_collection, bucket->h, &bucket->val); + } } zval_ptr_dtor(&retval); ZEND_HASH_FOREACH_END(); @@ -671,13 +810,20 @@ PHP_METHOD(Collection, filterNot) ARRAY_NEW_EX(new_collection, current); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (!zend_is_true(&retval)) { + if (!zend_is_true(&retval)) + { if (bucket->key) + { zend_hash_add(new_collection, bucket->key, &bucket->val); + } else if (HT_IS_PACKED(current)) + { zend_hash_next_index_insert(new_collection, &bucket->val); + } else + { zend_hash_index_add(new_collection, bucket->h, &bucket->val); + } } zval_ptr_dtor(&retval); ZEND_HASH_FOREACH_END(); @@ -699,13 +845,20 @@ PHP_METHOD(Collection, filterNotTo) SEPARATE_COLLECTION(dest_arr, dest); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (!zend_is_true(&retval)) { + if (!zend_is_true(&retval)) + { if (bucket->key) + { zend_hash_add(dest_arr, bucket->key, &bucket->val); + } else if (HT_IS_PACKED(current)) + { zend_hash_next_index_insert(dest_arr, &bucket->val); + } else + { zend_hash_index_add(dest_arr, bucket->h, &bucket->val); + } } zval_ptr_dtor(&retval); ZEND_HASH_FOREACH_END(); @@ -727,13 +880,20 @@ PHP_METHOD(Collection, filterTo) SEPARATE_COLLECTION(dest_arr, dest); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (zend_is_true(&retval)) { + if (zend_is_true(&retval)) + { if (bucket->key) + { zend_hash_add(dest_arr, bucket->key, &bucket->val); + } else if (HT_IS_PACKED(current)) + { zend_hash_next_index_insert(dest_arr, &bucket->val); + } else + { zend_hash_index_add(dest_arr, bucket->h, &bucket->val); + } } zval_ptr_dtor(&retval); ZEND_HASH_FOREACH_END(); @@ -750,17 +910,23 @@ PHP_METHOD(Collection, first) ZEND_PARSE_PARAMETERS_END(); zend_array* current = COLLECTION_FETCH_CURRENT(); if (zend_hash_num_elements(current) == 0) + { RETURN_NULL(); - if (EX_NUM_ARGS() == 0) { + } + if (EX_NUM_ARGS() == 0) + { HashPosition pos = 0; while (pos < current->nNumUsed && Z_ISUNDEF(current->arData[pos].val)) + { ++pos; + } RETURN_ZVAL(¤t->arData[pos].val, 1, 0); } INIT_FCI(2); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (zend_is_true(&retval)) { + if (zend_is_true(&retval)) + { zval_ptr_dtor(&retval); RETURN_ZVAL(&bucket->val, 1, 0); } @@ -786,9 +952,13 @@ PHP_METHOD(Collection, flatMap) ZEND_HASH_FOREACH_BUCKET(retval_p_arr, Bucket* bucket) Z_TRY_ADDREF(bucket->val); if (bucket->key) + { zend_hash_add(new_collection, bucket->key, &bucket->val); + } else + { zend_hash_next_index_insert(new_collection, &bucket->val); + } ZEND_HASH_FOREACH_END(); zval_ptr_dtor(&retval); ZEND_HASH_FOREACH_END(); @@ -815,9 +985,13 @@ PHP_METHOD(Collection, flatMapTo) ZEND_HASH_FOREACH_BUCKET(retval_p_arr, Bucket* bucket) Z_TRY_ADDREF(bucket->val); if (bucket->key) + { zend_hash_add(dest_arr, bucket->key, &bucket->val); + } else + { zend_hash_next_index_insert(dest_arr, &bucket->val); + } ZEND_HASH_FOREACH_END(); zval_ptr_dtor(&retval); ZEND_HASH_FOREACH_END(); @@ -830,18 +1004,27 @@ PHP_METHOD(Collection, flatten) ARRAY_NEW_EX(new_collection, current); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) zval* val = &bucket->val; - ELEMENTS_VALIDATE(val, ERR_SILENCED, { + ELEMENTS_VALIDATE(val, ERR_SILENCED, + { if (bucket->key) + { zend_hash_add(new_collection, bucket->key, &bucket->val); + } else + { zend_hash_next_index_insert(new_collection, &bucket->val); + } continue; }); ZEND_HASH_FOREACH_BUCKET(val_arr, Bucket* bucket) if (bucket->key) + { zend_hash_add(new_collection, bucket->key, &bucket->val); + } else + { zend_hash_next_index_insert(new_collection, &bucket->val); + } ZEND_HASH_FOREACH_END(); ZEND_HASH_FOREACH_END(); RETVAL_NEW_COLLECTION(new_collection); @@ -862,9 +1045,13 @@ PHP_METHOD(Collection, fold) ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) ZVAL_COPY_VALUE(¶ms[1], &bucket->val); if (bucket->key) + { ZVAL_STR(¶ms[2], bucket->key); + } else + { ZVAL_LONG(¶ms[2], bucket->h); + } zend_call_function(&fci, &fcc); zval_ptr_dtor(¶ms[0]); ZVAL_COPY_VALUE(¶ms[0], &retval); @@ -887,9 +1074,13 @@ PHP_METHOD(Collection, foldRight) ZEND_HASH_REVERSE_FOREACH_BUCKET(current, Bucket* bucket) ZVAL_COPY_VALUE(¶ms[1], &bucket->val); if (bucket->key) + { ZVAL_STR(¶ms[2], bucket->key); + } else + { ZVAL_LONG(¶ms[2], bucket->h); + } zend_call_function(&fci, &fcc); zval_ptr_dtor(¶ms[0]); ZVAL_COPY_VALUE(¶ms[0], &retval); @@ -925,17 +1116,26 @@ PHP_METHOD(Collection, get) zend_array* current = COLLECTION_FETCH_CURRENT(); zval* found = NULL; if (Z_TYPE_P(key) == IS_STRING) + { found = zend_hash_find(current, Z_STR_P(key)); + } else if (Z_TYPE_P(key) == IS_LONG) + { found = zend_hash_index_find(current, Z_LVAL_P(key)); - else { + } + else + { ERR_BAD_KEY_TYPE(); RETURN_NULL(); } if (found) + { RETURN_ZVAL(found, 1, 0); + } if (EX_NUM_ARGS() < 2) + { RETURN_NULL(); + } INIT_FCI(1); ZVAL_COPY_VALUE(¶ms[0], key); zend_call_function(&fci, &fcc); @@ -961,9 +1161,12 @@ PHP_METHOD(Collection, indexOf) zend_array* current = COLLECTION_FETCH_CURRENT(); INIT_EQUAL_CHECK_FUNC(element); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) - if (equal_check_func(element, &bucket->val)) { + if (equal_check_func(element, &bucket->val)) + { if (bucket->key) + { RETURN_STR(bucket->key); + } RETURN_LONG(bucket->h); } ZEND_HASH_FOREACH_END(); @@ -982,10 +1185,13 @@ PHP_METHOD(Collection, indexOfFirst) ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) ZVAL_COPY_VALUE(¶ms[0], &bucket->val); zend_call_function(&fci, &fcc); - if (zend_is_true(&retval)) { + if (zend_is_true(&retval)) + { zval_ptr_dtor(&retval); if (bucket->key) + { RETURN_STR(bucket->key); + } RETURN_LONG(bucket->h); } zval_ptr_dtor(&retval); @@ -1005,10 +1211,13 @@ PHP_METHOD(Collection, indexOfLast) ZEND_HASH_REVERSE_FOREACH_BUCKET(current, Bucket* bucket) ZVAL_COPY_VALUE(¶ms[0], &bucket->val); zend_call_function(&fci, &fcc); - if (zend_is_true(&retval)) { + if (zend_is_true(&retval)) + { zval_ptr_dtor(&retval); if (bucket->key) + { RETURN_STR(bucket->key); + } RETURN_LONG(bucket->h); } zval_ptr_dtor(&retval); @@ -1023,7 +1232,8 @@ PHP_METHOD(Collection, init) Z_PARAM_OPTIONAL Z_PARAM_ZVAL(elements) ZEND_PARSE_PARAMETERS_END(); - if (elements) { + if (elements) + { ELEMENTS_VALIDATE(elements, ERR_BAD_ARGUMENT_TYPE, return); RETURN_NEW_COLLECTION(elements_arr); } @@ -1055,9 +1265,13 @@ PHP_METHOD(Collection, keys) ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) zval val; if (bucket->key) + { ZVAL_STR(&val, bucket->key); + } else + { ZVAL_LONG(&val, bucket->h); + } zend_hash_next_index_insert(new_collection, &val); ZEND_HASH_FOREACH_END(); RETVAL_NEW_COLLECTION(new_collection); @@ -1073,17 +1287,23 @@ PHP_METHOD(Collection, last) ZEND_PARSE_PARAMETERS_END(); zend_array* current = COLLECTION_FETCH_CURRENT(); if (zend_hash_num_elements(current) == 0) + { RETURN_NULL(); - if (EX_NUM_ARGS() == 0) { + } + if (EX_NUM_ARGS() == 0) + { HashPosition pos = current->nNumUsed; while (pos <= current->nNumUsed && Z_ISUNDEF(current->arData[pos].val)) + { --pos; + } RETURN_ZVAL(¤t->arData[pos].val, 1, 0); } INIT_FCI(2); ZEND_HASH_REVERSE_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (zend_is_true(&retval)) { + if (zend_is_true(&retval)) + { zval_ptr_dtor(&retval); RETURN_ZVAL(&bucket->val, 1, 0); } @@ -1101,9 +1321,12 @@ PHP_METHOD(Collection, lastIndexOf) zend_array* current = COLLECTION_FETCH_CURRENT(); INIT_EQUAL_CHECK_FUNC(element); ZEND_HASH_REVERSE_FOREACH_BUCKET(current, Bucket* bucket) - if (equal_check_func(element, &bucket->val)) { + if (equal_check_func(element, &bucket->val)) + { if (bucket->key) + { RETURN_STR(bucket->key); + } RETURN_LONG(bucket->h); } ZEND_HASH_FOREACH_END(); @@ -1152,7 +1375,9 @@ PHP_METHOD(Collection, max) zend_array* current = COLLECTION_FETCH_CURRENT(); zval* max = zend_hash_minmax(current, bucket_compare_numeric, 1); if (max) + { RETURN_ZVAL(max, 0, 0); + } RETVAL_NULL(); } @@ -1171,12 +1396,16 @@ PHP_METHOD(Collection, maxBy) zend_hash_index_add(max_by, bucket - current->arData, &retval); ZEND_HASH_FOREACH_END(); zval* max = zend_hash_minmax(max_by, bucket_compare_numeric, 1); - if (max) { + if (max) + { zend_ulong offset = *(zend_ulong*)(max + 1); zval* ret = &(current->arData + offset)->val; RETVAL_ZVAL(ret, 1, 0); - } else + } + else + { RETVAL_NULL(); + } zend_hash_destroy(max_by); efree(max_by); } @@ -1191,7 +1420,9 @@ PHP_METHOD(Collection, min) zend_array* current = COLLECTION_FETCH_CURRENT(); zval* min = zend_hash_minmax(current, bucket_compare_numeric, 0); if (min) + { RETURN_ZVAL(min, 0, 0); + } RETVAL_NULL(); } @@ -1210,12 +1441,16 @@ PHP_METHOD(Collection, minBy) zend_hash_index_add(min_by, bucket - current->arData, &retval); ZEND_HASH_FOREACH_END(); zval* min = zend_hash_minmax(min_by, bucket_compare_numeric, 0); - if (min) { + if (min) + { zend_ulong offset = *(zend_ulong*)(min + 1); zval* ret = &(current->arData + offset)->val; RETVAL_ZVAL(ret, 1, 0); - } else + } + else + { RETVAL_NULL(); + } zend_hash_destroy(min_by); efree(min_by); } @@ -1266,7 +1501,8 @@ PHP_METHOD(Collection, none) zend_array* current = COLLECTION_FETCH_CURRENT(); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (zend_is_true(&retval)) { + if (zend_is_true(&retval)) + { zval_ptr_dtor(&retval); RETURN_FALSE; } @@ -1314,11 +1550,17 @@ PHP_METHOD(Collection, partition) zend_array* which = zend_is_true(&retval) ? first_arr : second_arr; Z_TRY_ADDREF(bucket->val); if (bucket->key) + { zend_hash_add(which, bucket->key, &bucket->val); + } else if (HT_IS_PACKED(current)) + { zend_hash_next_index_insert(which, &bucket->val); + } else + { zend_hash_index_add(which, bucket->h, &bucket->val); + } zval_ptr_dtor(&retval); ZEND_HASH_FOREACH_END(); zval first, second; @@ -1361,11 +1603,17 @@ PHP_METHOD(Collection, putAll) ZEND_HASH_FOREACH_BUCKET(elements_arr, Bucket* bucket) Z_TRY_ADDREF(bucket->val); if (bucket->key) + { zend_hash_update(current, bucket->key, &bucket->val); + } else if (HT_IS_PACKED(elements_arr)) + { zend_hash_next_index_insert(current, &bucket->val); + } else + { zend_hash_index_update(current, bucket->h, &bucket->val); + } ZEND_HASH_FOREACH_END(); } @@ -1377,28 +1625,39 @@ PHP_METHOD(Collection, reduce) Z_PARAM_FUNC(fci, fcc) ZEND_PARSE_PARAMETERS_END(); zend_array* current = COLLECTION_FETCH_CURRENT(); - if (zend_hash_num_elements(current) == 0) { + if (zend_hash_num_elements(current) == 0) + { ERR_BAD_SIZE(); RETURN_NULL(); } INIT_FCI(3); Bucket* bucket = current->arData; Bucket* end = bucket + current->nNumUsed; - for (; bucket < end; ++bucket) { + for (; bucket < end; ++bucket) + { if (Z_ISUNDEF(bucket->val)) + { continue; + } ZVAL_COPY(&retval, &(bucket++)->val); break; } - for (; bucket < end; ++bucket) { + for (; bucket < end; ++bucket) + { if (Z_ISUNDEF(bucket->val)) + { continue; + } ZVAL_COPY_VALUE(¶ms[0], &retval); ZVAL_COPY_VALUE(¶ms[1], &bucket->val); if (bucket->key) + { ZVAL_STR(¶ms[2], bucket->key); + } else + { ZVAL_LONG(¶ms[2], bucket->h); + } zend_call_function(&fci, &fcc); zval_ptr_dtor(¶ms[0]); } @@ -1413,28 +1672,39 @@ PHP_METHOD(Collection, reduceRight) Z_PARAM_FUNC(fci, fcc) ZEND_PARSE_PARAMETERS_END(); zend_array* current = COLLECTION_FETCH_CURRENT(); - if (zend_hash_num_elements(current) == 0) { + if (zend_hash_num_elements(current) == 0) + { ERR_BAD_SIZE(); RETURN_NULL(); } INIT_FCI(3); Bucket* start = current->arData; Bucket* bucket = start + current->nNumUsed - 1; - for (; bucket >= start; --bucket) { + for (; bucket >= start; --bucket) + { if (Z_ISUNDEF(bucket->val)) + { continue; + } ZVAL_COPY(&retval, &(bucket--)->val); break; } - for (; bucket >= start; --bucket) { + for (; bucket >= start; --bucket) + { if (Z_ISUNDEF(bucket->val)) + { continue; + } ZVAL_COPY_VALUE(¶ms[0], &retval); ZVAL_COPY_VALUE(¶ms[1], &bucket->val); if (bucket->key) + { ZVAL_STR(¶ms[2], bucket->key); + } else + { ZVAL_LONG(¶ms[2], bucket->h); + } zend_call_function(&fci, &fcc); zval_ptr_dtor(¶ms[0]); } @@ -1452,20 +1722,30 @@ PHP_METHOD(Collection, remove) ZEND_PARSE_PARAMETERS_END(); zend_array* current = COLLECTION_FETCH_CURRENT(); zval* found; - if (Z_TYPE_P(key) == IS_LONG) { + if (Z_TYPE_P(key) == IS_LONG) + { if (value == NULL) + { RETURN_BOOL(zend_hash_index_del(current, Z_LVAL_P(key)) == SUCCESS); + } found = zend_hash_index_find(current, Z_LVAL_P(key)); if (found == NULL || fast_equal_check_function(found, value) == 0) + { RETURN_FALSE; + } RETURN_BOOL(zend_hash_index_del(current, Z_LVAL_P(key)) == SUCCESS); } - if (Z_TYPE_P(key) == IS_STRING) { + if (Z_TYPE_P(key) == IS_STRING) + { if (value == NULL) + { RETURN_BOOL(zend_hash_del(current, Z_STR_P(key)) == SUCCESS); + } found = zend_hash_find(current, Z_STR_P(key)); if (found == NULL || fast_equal_check_function(found, value) == 0) + { RETURN_FALSE; + } RETURN_BOOL(zend_hash_del(current, Z_STR_P(key)) == SUCCESS); } ERR_BAD_KEY_TYPE(); @@ -1482,7 +1762,8 @@ PHP_METHOD(Collection, removeAll) ZEND_PARSE_PARAMETERS_END(); zend_array* current = COLLECTION_FETCH_CURRENT(); SEPARATE_CURRENT_COLLECTION(current); - if (EX_NUM_ARGS() == 0) { + if (EX_NUM_ARGS() == 0) + { zend_hash_clean(current); return; } @@ -1490,7 +1771,9 @@ PHP_METHOD(Collection, removeAll) ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); if (zend_is_true(&retval)) + { zend_hash_del_bucket(current, bucket); + } zval_ptr_dtor(&retval); ZEND_HASH_FOREACH_END(); } @@ -1505,7 +1788,8 @@ PHP_METHOD(Collection, retainAll) ZEND_PARSE_PARAMETERS_END(); zend_array* current = COLLECTION_FETCH_CURRENT(); SEPARATE_CURRENT_COLLECTION(current); - if (EX_NUM_ARGS() == 0) { + if (EX_NUM_ARGS() == 0) + { zend_hash_clean(current); return; } @@ -1513,7 +1797,9 @@ PHP_METHOD(Collection, retainAll) ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); if (!zend_is_true(&retval)) + { zend_hash_del_bucket(current, bucket); + } zval_ptr_dtor(&retval); ZEND_HASH_FOREACH_END(); } @@ -1525,16 +1811,26 @@ PHP_METHOD(Collection, reverse) ZEND_HASH_REVERSE_FOREACH_BUCKET(current, Bucket* bucket) Z_TRY_ADDREF(bucket->val); if (bucket->key) + { zend_hash_add(reversed, bucket->key, &bucket->val); + } else if (HT_IS_PACKED(current)) + { zend_hash_next_index_insert(reversed, &bucket->val); + } else + { zend_hash_index_add(reversed, bucket->h, &bucket->val); + } ZEND_HASH_FOREACH_END(); if (GC_REFCOUNT(current) > 1) + { GC_DELREF(current); + } else + { zend_array_destroy(current); + } COLLECTION_FETCH_CURRENT() = reversed; } @@ -1545,11 +1841,17 @@ PHP_METHOD(Collection, reversed) ZEND_HASH_REVERSE_FOREACH_BUCKET(current, Bucket* bucket) Z_TRY_ADDREF(bucket->val); if (bucket->key) + { zend_hash_add(reversed, bucket->key, &bucket->val); + } else if (HT_IS_PACKED(current)) + { zend_hash_next_index_insert(reversed, &bucket->val); + } else + { zend_hash_index_add(reversed, bucket->h, &bucket->val); + } ZEND_HASH_FOREACH_END(); RETVAL_NEW_COLLECTION(reversed); } @@ -1564,14 +1866,19 @@ PHP_METHOD(Collection, shuffle) ZEND_HASH_FOREACH_END(); size_t offset = 0; Bucket* bucket = shuffled->arData; - for (; offset < num_elements - 1; ++offset) { + for (; offset < num_elements - 1; ++offset) + { zend_long rand_idx = php_mt_rand_range(offset, num_elements - 1); zend_hash_bucket_renum_swap(&bucket[offset], &bucket[rand_idx]); } if (GC_REFCOUNT(current) > 1) + { GC_DELREF(current); + } else - zend_hash_destroy(current); + { + zend_array_destroy(current); + } COLLECTION_FETCH_CURRENT() = shuffled; } @@ -1585,7 +1892,8 @@ PHP_METHOD(Collection, shuffled) ZEND_HASH_FOREACH_END(); size_t offset = 0; Bucket* bucket = shuffled->arData; - for (; offset < num_elements - 1; ++offset) { + for (; offset < num_elements - 1; ++offset) + { zend_long rand_idx = php_mt_rand_range(offset, num_elements - 1); zend_hash_bucket_renum_swap(&bucket[offset], &bucket[rand_idx]); } @@ -1601,14 +1909,20 @@ PHP_METHOD(Collection, set) Z_PARAM_ZVAL(value) ZEND_PARSE_PARAMETERS_END(); zend_array* current = COLLECTION_FETCH_CURRENT(); - if (Z_TYPE_P(key) == IS_STRING) { + if (Z_TYPE_P(key) == IS_STRING) + { SEPARATE_CURRENT_COLLECTION(current); zend_hash_update(current, Z_STR_P(key), value); - } else if (Z_TYPE_P(key) == IS_LONG) { + } + else if (Z_TYPE_P(key) == IS_LONG) + { SEPARATE_CURRENT_COLLECTION(current); zend_hash_index_update(current, Z_LVAL_P(key), value); - } else + } + else + { ERR_BAD_KEY_TYPE(); + } } PHP_METHOD(Collection, single) @@ -1624,16 +1938,23 @@ PHP_METHOD(Collection, single) zend_array* current = COLLECTION_FETCH_CURRENT(); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (zend_is_true(&retval)) { + if (zend_is_true(&retval)) + { if (Z_TYPE(single) == IS_UNDEF) + { ZVAL_COPY_VALUE(&single, &bucket->val); + } else + { RETURN_NULL(); + } } zval_ptr_dtor(&retval); ZEND_HASH_FOREACH_END(); if (Z_TYPE(single) == IS_UNDEF) - RETURN_NULL() + { + RETURN_NULL(); + } RETVAL_ZVAL(&single, 1, 0); } @@ -1647,15 +1968,20 @@ PHP_METHOD(Collection, slice) zend_array* current = COLLECTION_FETCH_CURRENT(); ARRAY_NEW_EX(sliced, current); ZEND_HASH_FOREACH_VAL(elements_arr, zval* val) - if (Z_TYPE_P(val) == IS_LONG) { + if (Z_TYPE_P(val) == IS_LONG) + { zval* found = zend_hash_index_find(current, Z_LVAL_P(val)); - if (found) { + if (found) + { Z_TRY_ADDREF_P(found); zend_hash_next_index_insert(sliced, found); } - } else if (Z_TYPE_P(val) == IS_STRING) { + } + else if (Z_TYPE_P(val) == IS_STRING) + { zval* found = zend_hash_find(current, Z_STR_P(val)); - if (found) { + if (found) + { Z_TRY_ADDREF_P(found); zend_hash_add(sliced, Z_STR_P(val), found); } @@ -1720,7 +2046,8 @@ PHP_METHOD(Collection, take) ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_LONG(n) ZEND_PARSE_PARAMETERS_END(); - if (n < 0) { + if (n < 0) + { ERR_BAD_SIZE(); return; } @@ -1729,19 +2056,28 @@ PHP_METHOD(Collection, take) ARRAY_NEW(new_collection, n > num_elements ? num_elements : n); Bucket* bucket = current->arData; Bucket* end = bucket + current->nNumUsed; - for (; n > 0 && bucket < end; ++bucket) { + for (; n > 0 && bucket < end; ++bucket) + { if (Z_ISUNDEF(bucket->val)) + { continue; + } --n; Z_TRY_ADDREF(bucket->val); // Works for any zend_array, however, it doesn't make sense if you use any of these methods // on non-packed zend_arrays. if (bucket->key) + { zend_hash_add_new(new_collection, bucket->key, &bucket->val); + } else if (HT_IS_PACKED(current)) + { zend_hash_next_index_insert(new_collection, &bucket->val); + } else + { zend_hash_index_add_new(new_collection, bucket->h, &bucket->val); + } } RETVAL_NEW_COLLECTION(new_collection); } @@ -1752,7 +2088,8 @@ PHP_METHOD(Collection, takeLast) ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_LONG(n) ZEND_PARSE_PARAMETERS_END(); - if (n < 0) { + if (n < 0) + { ERR_BAD_SIZE(); return; } @@ -1763,25 +2100,37 @@ PHP_METHOD(Collection, takeLast) zend_long num_taken = n; Bucket* taken[num_taken]; // Note that the original element orders should be preserved as in kotlin. - for (; num_taken > 0 && idx > 0; --idx) { + for (; num_taken > 0 && idx > 0; --idx) + { Bucket* bucket = current->arData + idx - 1; if (Z_ISUNDEF(bucket->val)) + { continue; + } taken[--num_taken] = bucket; } memset(&taken[0], 0, num_taken * sizeof(Bucket*)); int i = 0; - for (; i < n; ++i) { + for (; i < n; ++i) + { Bucket* bucket = taken[i]; if (bucket == NULL) + { continue; + } Z_TRY_ADDREF(bucket->val); if (bucket->key) + { zend_hash_add_new(new_collection, bucket->key, &bucket->val); + } else if (HT_IS_PACKED(current)) + { zend_hash_next_index_insert(new_collection, &bucket->val); + } else + { zend_hash_index_add_new(new_collection, bucket->h, &bucket->val); + } } RETVAL_NEW_COLLECTION(new_collection); } @@ -1800,10 +2149,13 @@ PHP_METHOD(Collection, takeLastWhile) Bucket* taken[num_elements]; ZEND_HASH_REVERSE_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (zend_is_true(&retval)) { + if (zend_is_true(&retval)) + { zval_ptr_dtor(&retval); taken[--num_elements] = bucket; - } else { + } + else + { zval_ptr_dtor(&retval); break; } @@ -1813,14 +2165,22 @@ PHP_METHOD(Collection, takeLastWhile) for (; i < zend_hash_num_elements(current); ++i) { Bucket* bucket = taken[i]; if (bucket == NULL) + { continue; + } Z_TRY_ADDREF(bucket->val); if (bucket->key) + { zend_hash_add_new(new_collection, bucket->key, &bucket->val); + } else if (HT_IS_PACKED(current)) + { zend_hash_next_index_insert(new_collection, &bucket->val); + } else + { zend_hash_index_add_new(new_collection, bucket->h, &bucket->val); + } } RETVAL_NEW_COLLECTION(new_collection); } @@ -1837,15 +2197,24 @@ PHP_METHOD(Collection, takeWhile) ARRAY_NEW_EX(new_collection, current); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) CALLBACK_KEYVAL_INVOKE(params, bucket); - if (zend_is_true(&retval)) { + if (zend_is_true(&retval)) + { zval_ptr_dtor(&retval); if (bucket->key) + { zend_hash_add_new(new_collection, bucket->key, &bucket->val); + } else if (HT_IS_PACKED(current)) + { zend_hash_next_index_insert(new_collection, &bucket->val); + } else + { zend_hash_index_add_new(new_collection, bucket->h, &bucket->val); - } else { + } + } + else + { zval_ptr_dtor(&retval); break; } @@ -1871,9 +2240,13 @@ PHP_METHOD(Collection, toCollection) SEPARATE_COLLECTION(dest_arr, dest); ZEND_HASH_FOREACH_BUCKET(current, Bucket* bucket) if (bucket->key) + { zend_hash_add(dest_arr, bucket->key, &bucket->val); + } else + { zend_hash_next_index_insert(dest_arr, &bucket->val); + } ZEND_HASH_FOREACH_END(); RETVAL_ZVAL(dest, 1, 0); } @@ -1887,9 +2260,13 @@ PHP_METHOD(Collection, toPairs) zval pair, key; ZVAL_OBJ(&pair, obj); if (bucket->key) + { ZVAL_STR(&key, bucket->key); + } else + { ZVAL_LONG(&key, bucket->h); + } PAIR_UPDATE_FIRST(obj, &key); PAIR_UPDATE_SECOND(obj, &bucket->val); zend_hash_next_index_insert(new_collection, &pair);