From 440d3fdb63ea7c36155428cd4fe97da72713ae66 Mon Sep 17 00:00:00 2001 From: CismonX Date: Wed, 8 Aug 2018 00:43:09 +0800 Subject: [PATCH] Add `reduce()` and `reduceRight()`. Update signatures. Fix bugs. Update README. --- README.md | 2 +- src/collections_methods.c | 101 +++++++++++++++++++++++++++++++++++--- stubs/Collection.php | 48 ++++++++++-------- tests/039-reduce.phpt | 14 ++++++ 4 files changed, 136 insertions(+), 29 deletions(-) create mode 100644 tests/039-reduce.phpt diff --git a/README.md b/README.md index 6b55cae..ce9f2b1 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Method names and functionalities are inspired by [Kotlin.Collections](https://ko ### 2.1 Functionalities -See [stubs](stubs/) directory for signature of all classes and methods of this extension, with PHPDoc. +See [stubs](stubs/) directory for signature of all classes and methods of this extension, with PHPDoc. They can also serve as IDE helper. ### 2.2 PHP-style access diff --git a/src/collections_methods.c b/src/collections_methods.c index 192b3f8..332a200 100644 --- a/src/collections_methods.c +++ b/src/collections_methods.c @@ -113,9 +113,28 @@ /// Unused global variable. zval rv; -static zend_always_inline int bucket_compare_numeric(Bucket* op1, Bucket* op2) +static zend_always_inline int bucket_compare_numeric(const void* op1, const void* op2) { - return numeric_compare_function(&op1->val, &op2->val); + Bucket* b1 = (Bucket*)op1; + Bucket* b2 = (Bucket*)op2; + return numeric_compare_function(&b1->val, &b2->val); +} + +static zend_always_inline int bucket_compare_string(const void* op1, const void* op2) +{ + Bucket* b1 = (Bucket*)op1; + Bucket* b2 = (Bucket*)op2; + return string_compare_function(&b1->val, &b2->val); +} + +static zend_always_inline int bucket_compare_regular(const void* op1, const void* op2) +{ + Bucket* b1 = (Bucket*)op1; + Bucket* b2 = (Bucket*)op2; + zval result; + if (compare_function(&result, &b1->val, &b2->val) == FAILURE) + return 0; + return ZEND_NORMALIZE_BOOL(Z_LVAL(result)); } int count_collection(zval* obj, zend_long* count) @@ -1271,12 +1290,78 @@ PHP_METHOD(Collection, putAll) PHP_METHOD(Collection, reduce) { - + zend_fcall_info fci; + zend_fcall_info_cache fcc; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + zend_array* current = COLLECTION_FETCH_CURRENT(); + if (zend_hash_num_elements(current) == 0) { + ERR_BAD_SIZE(); + RETURN_NULL(); + } + INIT_FCI(3); + zval acc; + Bucket* bucket = current->arData; + Bucket* end = bucket + current->nNumUsed; + for (; bucket < end; ++bucket) { + if (Z_ISUNDEF(bucket->val)) + continue; + ZVAL_COPY_VALUE(&acc, &(bucket++)->val); + break; + } + for (; bucket < end; ++bucket) { + if (Z_ISUNDEF(bucket->val)) + continue; + ZVAL_COPY_VALUE(¶ms[0], &acc); + 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(&acc); + ZVAL_COPY_VALUE(&acc, &retval); + } + RETVAL_ZVAL(&retval, 0, 0); } PHP_METHOD(Collection, reduceRight) { - + zend_fcall_info fci; + zend_fcall_info_cache fcc; + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + zend_array* current = COLLECTION_FETCH_CURRENT(); + if (zend_hash_num_elements(current) == 0) { + ERR_BAD_SIZE(); + RETURN_NULL(); + } + INIT_FCI(3); + zval acc; + Bucket* start = current->arData; + Bucket* bucket = start + current->nNumUsed - 1; + for (; bucket >= start; --bucket) { + if (Z_ISUNDEF(bucket->val)) + continue; + ZVAL_COPY_VALUE(&acc, &(bucket--)->val); + break; + } + for (; bucket >= start; --bucket) { + if (Z_ISUNDEF(bucket->val)) + continue; + ZVAL_COPY_VALUE(¶ms[0], &acc); + 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(&acc); + ZVAL_COPY_VALUE(&acc, &retval); + } + RETVAL_ZVAL(&retval, 0, 0); } PHP_METHOD(Collection, remove) @@ -1365,7 +1450,7 @@ PHP_METHOD(Collection, reverse) 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_index_add(reversed, bucket->h, &bucket->val); ZEND_HASH_FOREACH_END(); if (GC_REFCOUNT(current) > 1) GC_DELREF(current); @@ -1385,7 +1470,7 @@ PHP_METHOD(Collection, reversed) 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_index_add(reversed, bucket->h, &bucket->val); ZEND_HASH_FOREACH_END(); RETVAL_NEW_COLLECTION(reversed); } @@ -1571,7 +1656,7 @@ PHP_METHOD(Collection, takeLast) continue; taken[--num_taken] = bucket; } - memset(&taken[0], NULL, num_taken * sizeof(Bucket*)); + memset(&taken[0], 0, num_taken * sizeof(Bucket*)); int i = 0; for (; i < n; ++i) { Bucket* bucket = taken[i]; @@ -1607,7 +1692,7 @@ PHP_METHOD(Collection, takeLastWhile) else break; ZEND_HASH_FOREACH_END(); - memset(&taken[0], NULL, num_elements * sizeof(Bucket*)); + memset(&taken[0], 0, num_elements * sizeof(Bucket*)); int i = 0; for (; i < zend_hash_num_elements(current); ++i) { Bucket* bucket = taken[i]; diff --git a/stubs/Collection.php b/stubs/Collection.php index 251364d..baa96a2 100644 --- a/stubs/Collection.php +++ b/stubs/Collection.php @@ -624,7 +624,7 @@ class Collection implements ArrayAccess, Countable * Accumulates value starting with the first element and applying operation from left to right to * current accumulator value and each element. * - * @param $operation ($acc, $value, $key) -> $new_acc + * @param callable $operation ($acc, $value, $key) -> $new_acc * @return mixed */ function reduce($operation) {} @@ -633,7 +633,7 @@ class Collection implements ArrayAccess, Countable * Accumulates value starting with the last element and applying operation from right to left to * each element and current accumulator value. * - * @param $operation ($acc, $value, $key) -> $new_acc + * @param callable $operation ($acc, $value, $key) -> $new_acc * @return mixed */ function reduceRight($operation) {} @@ -712,82 +712,90 @@ class Collection implements ArrayAccess, Countable function slice($keys) {} /** - * Sorts the collection in-place according to the natural order of its elements. + * Sorts the collection in-place according to the specified order of its elements. * + * @param int $order[optional] * @return void */ - function sort() {} + function sort($order = SORT_REGULAR) {} /** - * Sorts elements in the collection in-place according to natural sort order of the value returned + * Sorts elements in the collection in-place according to the specified order of the value returned * by specified selector function. * * @param callable $selector ($value, $key) -> $new_value + * @param int $flags[optional] * @return void */ - function sortBy($selector) {} + function sortBy($selector, $flags = SORT_REGULAR) {} /** - * Sorts elements in the collection in-place descending according to natural sort order of the + * Sorts elements in the collection in-place descending according to the specified order of the * value returned by specified selector function. * * @param callable $selector ($value, $key) -> $new_value + * @param int $flags[optional] * @return void */ - function sortByDescending($selector) {} + function sortByDescending($selector, $flags = SORT_REGULAR) {} /** - * Sorts elements in the collection in-place descending according to their natural sort order. + * Sorts elements in the collection in-place descending according to the specified order. * + * @param int $flags[optional] * @return void */ - function sortDescending() {} + function sortDescending($flags = SORT_REGULAR) {} /** * Sorts elements in the collection in-place according to the order specified with comparator. * - * @param callable $comparator ($value_1, $value_2, $key_1, $key_2) -> int + * @param callable $comparator (Pair($key, $value), Pair($key, $value)) -> int * @return void */ function sortWith($comparator) {} /** - * Returns a collection of all elements sorted according to their natural sort order. + * Returns a collection of all elements sorted according to the specified order. * + * @param int $flags[optional] * @return Collection */ - function sorted() {} + function sorted($flags = SORT_REGULAR) {} /** - * Returns a collection of all elements sorted according to natural sort order of the + * Returns a collection of all elements sorted according to the specified order of the * value returned by specified selector function. * * @param callable $selector ($value, $key) -> $new_value + * @param int $flags[optional] * @return Collection */ - function sortedBy($selector) {} + function sortedBy($selector, $flags = SORT_REGULAR) {} /** - * Returns a collection of all elements sorted descending according to natural sort order + * Returns a collection of all elements sorted descending according to the specified order * of the value returned by specified selector function. * * @param callable $selector ($value, $key) -> $new_value + * @param int $flags[optional] * @return Collection */ - function sortedByDescending($selector) {} + function sortedByDescending($selector, $flags = SORT_REGULAR) {} /** - * Returns a collection of all elements sorted descending according to their natural sort order. + * Returns a collection of all elements sorted descending according to the specified order. * * @param callable $selector ($value, $key) -> $new_value + * @param int $flags[optional] * @return Collection */ - function sortedDescending($selector) {} + function sortedDescending($selector, $flags = SORT_REGULAR) {} /** * Returns a collection of all elements sorted according to the specified comparator. * - * @param callable $comparator ($value_1, $value_2, $key_1, $key_2) -> int + * @param callable $comparator (Pair($key, $value), Pair($key, $value)) -> int * @return Collection */ function sortedWith($comparator) {} diff --git a/tests/039-reduce.phpt b/tests/039-reduce.phpt new file mode 100644 index 0000000..64c6869 --- /dev/null +++ b/tests/039-reduce.phpt @@ -0,0 +1,14 @@ +--TEST-- +Test Collection::reduce() and Collection::reduceRight(). +--FILE-- +reduce($reduce_cb) != 'ab1c2d3e4') + echo 'Collection::reduce() failed.', PHP_EOL; +if ($collection->reduceRight($reduce_cb) != 'ed3c2b1a0') + echo 'Collection::reduceRight() failed.', PHP_EOL; +?> +--EXPECT--